作者:王博文 | 旷视 MegEngine 架构师
一、背景
对于深度学习框架来说,网络的训练 / 推理工夫是用户十分看中的。在理论生产条件下,用户设计的 NN 网络是千差万别,即便是同一类数学计算,参数也各不相同。如果没有针对性的优化,框架就齐全丢失竞争力。因而,在一类数学计算中,开发者们会开发多种高效的算法,别离实用于不同的参数,以保障网络的性能。接下来开发者们须要解决一个新问题,当计算参数确定当前,如何让最快的算法执行该计算。
大部分框架靠先验的教训抉择算法,MegEngine 亦总结有优良的先验经验值,实现计算时主动抉择算法。然而依附教训不能保障肯定抉择了最快的算法。很多理论场景中,用户心愿网络有最极致的性能。为此,MegEngine 设计了专门的流程,能够为每个计算主动抉择最快的算法,从而保障整个网络的运行工夫最短。并且同时可能将计算的参数和其对应的算法信息以及设施信息记录到内存或文件,当用户再次运行网络时,能够间接获取性能最好的算法。这一晋升性能的流程被称为 Fast Run,它能让 MegEngine 的用户运行不同的网络时都能播种最好的性能。
二、Fast Run 简述
目前,支流的框架简直都应用了算子(Operator)的概念来形象数学计算,如卷积算子,矩阵乘算子等。MegEngine 也应用了 算子 这一概念。此外,在底层,咱们开发了名为 MegDNN 的计算库,用以实现理论的数学计算。MegDNN 仅提供数学计算能力。MegDNN 的顶层也是依照算子的概念组织的,对不同的后端,别离封装了 MegDNN 算子。一个 MegDNN 算子外部则可能有多个该算子的算法,MegEngine 将算法形象为 Algorithm,一个 Algorithm 对象能够实现该算子的计算。
以卷积算子为例,ARM 上,MegEngine 实现了十分通用的 Im2col 算法,有特定条件下性能卓越的 Winograd 算法,有在小尺寸卷积时高性能的 Direct 间接卷积算法等。CUDA 上,有调用 cuDNN 库函数的办法等等。从 MegEngine 算子到 MegDNN 算子再到算法的关系如下图所示:
一个 MegEngine 算子可能持有一个或多个 MegDNN 算子来实现计算,一个 MegDNN 算子须要从多个算法对象中抉择一个来执行计算。为了极致的计算性能,须要在开始网络计算之前,给 MegDNN 算子选好最快的算法。
Fast Run 的思路很间接,在网络计算开始之前,将每个 MegDNN 算子中所有可行的算法全副运行一次(Profiling),并将性能数据记录下来,将最快的算法设置给 MegDNN 算子。Fast Run 成立的前提条件是算法运行工夫是稳固的,这样比拟每个算法的 Profiling 数据才有意义。
最初是确定 Fast Run 执行的工夫点。MegEngine 有对立的内存治理,各 MegEngine 算子须要在计算开始前向内存布局单元申请足够的计算时内存,这一内存包含了其外部的 MegDNN 算子计算时须要的内存,而 MegDNN 算子计算时须要的内存齐全由算法决定。这就要求,MegDNN 此刻曾经确定了将要应用的算法。天然地,MegEngine 抉择在调用该接口之前执行 Fast Run 流程。这样,当 Fast Run 流程实现时,各 MegDNN 算子都设置了性能最好的算法。
Fast Run 执行的代价是显然的,它会显著减少第一次网络执行的工夫。Fast Run 的流程如下图:
Fast Run 有上面两种应用形式,区别在于上图中写入的 Cache 文件不同:
离线 Fast Run,离线 Fast Run 分两步,别离在不同的过程中实现。第一步先将整个网络计算执行一遍,这一过程中,Fast Run 会将各个算法的性能数据写到一个专门的数据结构中,最初数据被对立写入一个 Cache 文件,随后过程退出,这个过程称之为“搜参”。第二步,加载同样的网络,通过 MegEngine 的接口将 Cache 文件读入。能够看出,离线 Fast Run 甚至能够在不同的设施上进行。
在线 Fast Run,在线 Fast Run 在同一个过程实现的。前半段与离线 Fast Run 的流程雷同,Fast Run 后,各算法的性能数据保留在内存中的一个数据结构之中。此时,过程不会退出。后续能够给网络加载不同的输出数据,此时各 MegDNN 算子中已设置好性能最好的算法。并且,也能够初始化另外的网络,亦能够像离线 Fast Run 的后半局部一样,从以后的数据结构中读取算法。
总的来说,Fast Run 提供搜参和记录的性能。它的作用是给网络中的各个 MegDNN 算子抉择以后参数下性能最好的算法。因为 Fast Run 对每个 MegDNN 算子执行同样的操作,因而它在前向推理和反向流传时都能应用。目前,MegEngine 反对 CUDA、CPU、ROCM 三个后端的 Fast Run,MegEngine 的用户们在训练和部署时,均宽泛应用 Fast Run。
三、Fast Run 原理
Fast Run 中,Profiling 一个 MegDNN 算子并设置算法,会经验 4 个步骤,其流程如下图示:
这一流程中,须要留神一些细节:
1、递归搜参:MegDNN 中普遍存在算子嵌套的状况。例如,Convolution 算子中,Im2col 算法会应用 MegDNN 的 MatMul 算子执行矩阵乘计算。那么,Convolution 的性能间接受到 MatMul 性能的影响。能够看到,在 Profiling 一个 Convolution 算子之前,须要 MatMul 算子执行的性能数据已知。为了解决这个问题,Fast Run 应用了递归的形式,来解决搜参时的算子嵌套问题。如上图中虚线框所示,一个 MegDNN 算子,在获取所有可用算法之后,会调用每个算法的接口,询问该算法是否依赖子算子并保留相干后果,若最终相干后果不为空,则会先对子算子进行一次 Profiling,尔后,再 Profiling 顶层的算子时,其应用的子算子会有最优的算法保留在 Cache 中。
2、Fast Run 性能数据保留:Fast Run 性能数据存取离不开 Cache。MegEngine 提供了两种 PersistentCache,两种 Cache 区别于数据保留的地位(内存或是文件)。Cache 的构造如下图所示:
MegEngine 中,PersistentCache 对象是单例的,两种 Cache 都保障线程平安。Cache 保护一个从 category 信息到一个汇合的映射的汇合,此处 category 是一个后端的记录信息。Category 是一个字符串,由后端信息和算子类型拼接取得,后端信息 由设施辨别,例如 CUDA 的后端信息由设施名称、NVIDIA 驱动版本和 CUDA 运行时库版本信息组成;CPU 作为后端时,则只记录设施名称。MegEngine 中只有 CUDA、CPU、ROCM 三种类型有对应的 categoty 生成,这也是 MegEngine 目前仅反对在 CUDA、CPU、ROCM 三个后端反对 Fast Run 的起因。 算子类型 由算子名称、Cache 版本信息两局部组成。
一个 category 映射到一个汇合,该汇合保护单个 MegDNN 算子的信息到其所有可用算法的 Profiling 后果的映射。该汇合的 key 值 由 MegDNN 算子的所有输出 Tensor 的尺寸和算子的全副参数组成(这些参数可能齐全决定一个算法是否可用)。value 值 是一个数组,保留每个 Profiling 过的算法的工夫、所需额定的空间等信息,并排序。排序时,以运行工夫进行升序排列,并且保障了序列中每个算法应用的内存必须小于其前一个算法应用的内存 – 这样序列中不存在一个算法既慢于另一个算法,又应用更多的内存。一个 Cache 中能够存在不同后端的 Fast Run 后果,只有它们的 category 不同。
在一些常见的模型上,推理时敞开和开启 Fast Run,性能体现如下:
从工程落地中 Fast Run 的应用状况来看,绝大部分场景下,能显著升高网络运行工夫。
四、Fast Run 应用
MegEngine 可配置的参数泛滥,很多都是工程落地的解决办法,在工业上通过大量的实际。其中一些参数与 Fast Run 的应用有亲密的关系,这里具体论述它们的应用。
4.1 开启 Fast Run
源代码级别应用 Fast Run 能够参照 MegEngine 自带的可执行程序 load_and_run,如果仅关注利用 load_and_run 测试模型,有上面两个参数须要应用:
- –full-run/–fast-run,搜参的两种模式,需用户抉择其中一种模式,两者的区别在于 Profiling 时,生成的 MegDNN 算子的可用算法集大小不同。–full-run 时,会 Profiling MegDNN 算子内所有的可用算法,包含最奢侈的算法(MegDNN 算子至多有一个算法,保障任何参数下均可用,运行慢)。–fast-run 则会排除奢侈算法。如果想要缩小 Profiling 的工夫开销,能够抉择应用 –fast-run 模式,此时须要留神的是,如果网络中有参数过于非凡的算子,则该算子可能面临没有可用算法的状况(优化过的算法不可用、奢侈的算法被排除),此时 MegEngine 会报出“没有可用算法”的谬误并退出。
- –fast-run-algo-policy,指定 Cache 文件的门路,文件中的性能数据会被读入内存,被全局惟一的 PersistentCache 对象持有。过程退出前,PersistentCache 中的性能数据会全副写入该文件。
两个参数能够独自应用,也能够一起应用:
- 独自应用 –full-run/–fast-run,Profiling 数据保留在内存中。
- 两者一起应用,文件中的性能数据首先会被读入内存。如果文件为空,所有 MegDNN 算子实现搜参后,性能数据写回文件。如果文件不为空,且某个 MegDNN 算子能从 Cache 中查问到性能数据,则不会进行搜参,余下不能查到性能数据的,则会搜参。这样实现了断点搜参的性能,MegEngine 称之为“续搜“。如果 Fast Run 时程序因为某些起因异样退出,”续搜“能使 Fast Run 在下一次可能连上。“续搜”也能让多个模型的性能数据能够合并在一个 Cache 文件中。如果所有 MegDNN 算子都能从 Cache 中查到性能数据,则搜参不会产生,网络具备最好的性能。
- 独自应用 –fast-run-algo-policy,文件中的性能数据首先会被读入内存,如果 Cache 中没有记录,不“续搜”,以经验值设置 MegDNN 算子的算法,性能可能不是最优。
在应用 Fast Run 时,能够配合 –verbose 一起应用,程序将具体打印 Fast Run 时的调试信息,包含 MegDNN 算子的名称,输入输出的尺寸信息,设置的算法名称等。如果发现性能不合乎预期,比方当加载的模型和 Cache 文件不匹配时,通常会产生“续搜”,造成网络执行工夫很长的假象。因而,咱们强烈推荐在此时应用 –verbose 参数来察看程序工作是否合乎预期。
4.2 算法属性
MegDNN 中某些算法具备独特的属性,会影响向 MegDNN 算子设置算法,以后应用的 属性 有:
- REPRODUCIBLE:具备 REPRODUCIBLE 属性的算法,可保障计算结果比特对齐。Fast Run 中,在从 Cache 中读算法信息时提供了对 REPRODUCIBLE 属性的反对。设置 –reproducible,Fast Run 会从 Cache 中抉择性能最好的且具备 REPRODUCIBLE 属性的算法。在 Profiling 阶段,并不辨别算法是否 REPRODUCIBLE,这样 Cache 中的算法既有 REPRODUCIBLE 属性的,也有非 REPRODUCIBLE 属性的,具备肯定的泛用性。
- NAIVE:只有 MegDNN 中最奢侈的算法具备 NAIVE 属性。–full-run 和 –fast-run 的区别就在于 –fast-run 通过该属性筛除了运行最慢的奢侈算法。
4.3 weight 前解决
有些算法,在计算时须要对数据进行辅助转换。其中,对权重 weight 的转换能够是一次性的,这样能够节俭运行工夫。例如 Winograd 算法,其权重能够在进行卷积计算之前转好,节约相当一部分运行时的性能开销。MegEngine 在 GraphCommonOptimizeOptions 中提供了 weight_preprocess 选项来反对部署时权重的提前转换性能。一旦设置 weight_preprocess,对于那些 weight 可能提前转换的算法,其性能数据将不会蕴含权重转换的工夫。简略的说,在搜参阶段设置 weight_preprocess,会影响算法的性能数据,从而 Cache 中算法的性能数据排序可能不同。如果 Cache 是在开启 weight 前解决的状况下搜参失去,部署时务必要开启 weight 前解决以取得更好的性能,否则有性能降落的危险。Fast Run 与 weight 前解决不是必须的关系,两者能够离开应用。不过通常状况下,两者联合应用能够取得更好的性能.
4.4 Fast Run 版本
Fast Run 的版本信息以字符串的模式示意在 Cache 的 category 中。Cache 具备兼容性,能够容许不同的版本的 MegEngine 下的搜参后果汇合在同一个 Cache 中,Cache 中看到的是不同的 category。然而用户在应用过程,仍然须要留神 Fast Run 的版本。个别地,如果 MegDNN 的算法产生了删除或者是属性的变动,Fast Run 的版本信息会发生变化。Fast Run 版本信息变动后,须要从新搜参。
附:
GitHub:MegEngine 天元
官网:MegEngine- 深度学习,简略开发
欢送退出 MegEngine 技术交换 QQ 群:1029741705