有些相对神经网络模型训练之后需要集成到线上作为线上服务的一部分。如果网络结构比较简单,通常会把网络中的权重解析出来,并用C++实现模型的前向过程(inference)。由于要成为线上服务,计算约耗时越多,就意味着要更多更好的机器资源,要花出去的真金白银也就越多,所以通常inference过程对效率的要求会很高,这就需要对前向过程进行严苛的优化。

    一般我们会在程序的各个计算模块中加一些耗时统计逻辑,找出耗时最高的那些部件,针对性的进行优化。

常用的优化方法:

0 多线程:

  没有先后关系的运算均可以利用多线程并行。在tensorflow的运行时中,开辟了inter、和intra两个线程池,分别进行op间和op内的并行。

  比如某个图像经过n个卷积核卷积操作得到n个featuremap,这n次卷积操作是可以并行的op,某个卷积核在输入图上的多次移动乘加过程是op内的可并行操作。如果4个卷积核,每个卷积核在输入图中有8个完整卷积操作,那么可以拆到4*8=32个线程中去做。

1 指令优化:

使用SIMD对乘加操作进行优化。

intel cpu上用avx2、avx512指令进行操作。嵌入式设备上用neon指令进行对应的代码优化。通常需要显式地对一些逻辑进行指令级别的改写。

比如avx2可以一次性对进行256(32*16)位长数据进行操作,也就是说一个avx2乘指令可以一次性完成16个32位的操作(乘或加),fma(Fused Multiply-Add)指令需要三个向量参数va,vb,vc,其效果等价于表达式(va∗vb)+vc,如一个fma指令 tgt = _mm256_fmadd_ps(op0, op1, tgt);样例可参考lpcnet中的vec?.h系列,如https:///mozilla/LPCNet/blob/master/src/vec_avx.h

使用fma指令需要在g++编译参数中增加 -mfma

2 gpu加速

如果有机器有nvidia的独立显卡,可以考虑改写部分函数为cudac实现,因为显卡的流处理器的数量比cpu核心数往往高2个数量级。

3 近似计算

如用perf等工具分析。某些频繁使用的数学计算可以查表近似。这个在计算运算库中往往会有体现。如e的指数计算。

4 调用加速运算库

一些常用的数学计算在加速库中会有快速解法,比如算sigmoid激活函数、三角函数等,有一些查表的操作做替代。这些加速库有mkl、openblas、lapack等。mkl中有fft的加速实现。

5 浮点运算定点化

整数乘法的计算比浮点数的乘法复杂度通常要小,把浮点数按照取值区间归一化到定点数范围,然后把浮点数的高频乘法变成定点数的乘法。

6 网络裁剪、网络维度缩小

7 网络稀疏化

另外:一些常用的普通优化要记得做,比如编译时-O3。结构体对齐