Page 336 - 《软件学报》2020年第9期
P. 336

董晓  等:面向稀疏卷积神经网络的 GPU 性能优化方法                                                     2957


             其次,优化效果也与算子本身的特征以及批大小有关.批大小为 64 时,除 lenet-conv1 以外,我们的方法对
         cuDNN 的加速效果在不同算子间比较稳定.我们通过 profiling 发现:虽然 cuDNN 对 lenet-conv1 和 lenet-conv2
         均采用了 fft 算法,但内部使用了不同的 kernel 实现(lenet-conv1:cgemm_strided_batched_ sm35_ldg_nt_
         64x8x64x16x16;lenet-conv2:fermiPlusCgemmLDS128_batched),造成了明显的性能差异.尽管 lenet-conv1 的计算
         需求仅为 lenet-conv2 的约 18%,但执行时间却是 lenet- conv2 的 1.3 倍.另外, cuBLAS 中的矩阵乘法 kernel 在计
         算量较大的 5 个算子(2 个 resnet 算子和 3 个 vgg 算子)上的性能显著好于剩余的计算量较小的 5 个算子,平均
         性能差异可达 12.6 倍.因此,我们的方法相对 cuBLAS的加速效果对于小规模算子更加明显.cuSPARSE和 Escoin
         也有类似的现象,在计算量较大的算子和其他算子上的平均性能有显著的差异.
             批大小为 1 时,卷积参数失去了在不同输入数据之间的复用机会.第 1 次读取卷积参数会导致片上的常量
         缓存发生缺失,需要从位于片外 DRAM 上的常量内存中进行读取,延迟很高.而当批大小为 64 时,这一开销可以
         被后续通过常量缓存的加速访问分摊.因此总体来看,批大小为 1 时的性能收益较 64 时有所下降.在 3 个来自
         vgg 的算子上,我们的方法取得收益更加明显.这是因为 vgg 算子的计算结果规模很大,经过任务划分后允许更
         多 GPU 线程并发执行,所以可以通过线程级并行更好地掩盖访存延迟;resnet 算子虽然访问的参数规模与 vgg
         算子类似,但由于计算结果规模显著小于 vgg 算子,因此线程级并行机会更少.其他算子虽然卷积参数数目较少,
         但其输出结果规模也远小于 vgg 算子,所以也难以通过线程间的并行有效掩盖访问延迟.另外,对于 lenet-conv1
         算子,cuDNN 采用 implicit gemm [15] 算法替换了 fft,使得 lenet-conv1 的执行时间少于 lenet-conv2,消除了批大小
         为 64 时 lenet-conv1 与 lenet-conv2 之间的性能倒挂现象.此外,相对于 Escoin,我们的方法在 alexnet-conv1 上的
         收益非常显著.由于 Escoin 内部根据数据形状、批大小和参数的稀疏程度等信息硬编码了一些算子到具体
         kernel 的映射规则,这些规则并不准确,负责 alexnet-conv1 的 kernel 中存在大量非合并的全局内存访问,导致其
         性能发生了显著降低.
         3.3   与结构化剪枝方法的对比分析
             在这一节中,我们与基于通道剪枝和卷积核剪枝的结构化剪枝方法进行比较.具体来说,对每一个实验算
         子,我们考虑了删除一半输入通道(pruning channel,简称 PC)、删除一半卷积核(pruning filter,简称 PF)以及同时
         删除一半输入通道和一半卷积核(both)这 3 种情况.由于结构化剪枝产生的是规模更小的稠密参数,我们使用
         cuDNN 实现了上面 3 种剪枝后的卷积,并与我们生成的代码进行了性能对比.由于 PC 和 PF 可以移除卷积中
         50%的参数,我们使用了在 0.5 稀疏程度下生成的优化代码进行对比;对于同时删除输入通道和卷积核的情况
         (both),其稀疏程度可达 0.75,我们使用 0.8 稀疏程度下的代码对比.
             图 9 展示了批大小为 64 和批大小为 1 时的实验结果.对于输入通道数目很小且是奇数的算子(alexnet-
         conv1,lenet-conv1,vgg-conv1),我们不对其输入通道进行删除,因此 PC 跳过了这些算子;同时,由于不删除输入通
         道,因此 Both 和 PF 在这些算子上性能一致.对于每一个算子,我们将各个方法的性能相对稠密情况下 cuDNN 的
         性能(dense)做了归一化,数值大于 1 表示性能好于稠密情况下的 cuDNN.
             相对于 PF 与 PC,我们生成的代码在大部分算子上实现了更好的性能.
             •   在批大小为 64 时,相对 PC 和 PF 在 10 个算子上分别实现了平均 1.3 倍和 2.1 倍的加速;相对 Both 实
                现了 2.1 倍的加速;
             •   批大小为 1 时结果类似,对 PC,PF 和 Both 的加速比分别为 1.3 倍、2.1 倍和 1.5 倍.
             我们也发现:由于 cuDNN 对内部实现方法的选择策略等原因,算子在不同剪枝情况下的性能变化与计算量
         不完全一致.例如:在批大小为 1 时,对于 alexnet-conv2,cuDNN 对 PF 对应的卷积选择使用 fft 实现,而对 PC 和
         Both 都使用了 implicit gemm 的方法.另外,通过 nvprof 我们发现,PC 和 Both 实际上执行的单精度浮点操作数目
         十分接近.我们猜测:为了利用预先调优的内部 kernel 实现,cuDNN 可能对 Both 的情况作了数据补齐,导致了冗
         余计算.对批大小为 1 时的 resnet-conv1 算子,cuDNN 对 PC 和 Both 均使用显式的 im2col 接 GEMM 的方法实
         现,而对 PF 使用了 implicit gemm 的方法,造成 PC 与 PF 虽然计算量类似,但 PC 性能显著差于 PF;而 Both 虽然
         计算需求仅为 PF 的一半,但性能仍然不及 PF.
   331   332   333   334   335   336   337   338   339   340   341