Press "Enter" to skip to content

OpenPPL 性能优化解析之 X86 篇

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

 

 

自 OpenPPL 发布以来,经过持续的迭代,目前在 Intel 和 AMD 平台上均达到了领先业界的推理速度。仓库地址:

 

:link:  https://github.com/openppl-public/ppl.nn

 

近半年来 OpenPPL 的 X86 后端的主要工作是对已经支持的 FMA 和 AVX512F 指令集上做更细致的调优工作,包括算子层面以及图优化层面,从而使性能得到大幅提升。

 

同时,OpenPPL 也基本 完成对 SSE 指令集的支持, 旨在支持一些较老架构或者移动端的 Intel 处理器,比如 nehelem/westmere 架构的服务器 CPU,以及 Apollo Lake 等嵌入式处理器;

 

另外, OpenPPL SSE 版本,也对国产兆芯处理器做了支持, 可以跑基本的网络模型,性能近似于 ARM 的 a57/a72/a73。

 

具体更新如下:

 

Depthwise 卷积融合

 

Depthwise 卷积,也叫逐通道卷积,是从 Mobilenet 系列开始出现的一种参数比较特殊的卷积,对每个通道进行独立的卷积计算,不进行通道之间的累加,可以视作一种极端参数的分组卷积。

 

通常 Depthwise 卷积和 Pointwise 卷积(1×1 卷积)合在一起称为深度可分离卷积,相比于一般的 3×3 卷积,这种卷积算法大幅降低了计算量,但是并没有损失太多精度,是专门为移动端设备设计的。

 

由于在算力要求上的独特优势,类似 mobilenet 的结构已经越来越广泛地应用在各种设备上,以满足大家对推理速度的需求,其中也包括了 X86 架构的服务器。X86 架构的服务器通常是不缺少算力的,单核算力为移动端设备的 2~4 倍以上。

 

但是相较于算力来说,X86 服务器的单核内存带宽,以及全核心运行时的每核平均内存带宽,相较于移动设备是不增反降的。

 

 

△ MobileNetV2 中深度可分离卷积对 3×3 卷积进行的变换

 

根据深度可分离卷积对一般的 3×3 卷积做出的变换,可以推导出计算访存比的变化,关于计算方寸比的事情可以参考:

 

《OpenPPL 高性能推理引擎技术探讨:性能优化概要》

 

《深度学习模型大小与模型推理速度的探讨》

 

3×3 卷积的计算访存比为:

 

深度可分离卷积的计算访存比为:

 

深度可分离卷积由于引入一层了 Dw 卷积,这里增加了额外的访存开销。我们假设变换前的 3×3 卷积已经是带宽受限的情况下,1×1 卷积和 Dw 卷积也必然是带宽受限的,因为这两种卷积的计算访存比都比 3×3 卷积要小的多,这时候理论上的推理时间就会变成 3×3 卷积的两倍。(实际可能大于也可能小于两倍,需要看具体参数和硬件配置)

 

OpenPPL 为了优化这种情况开发了针对这种特殊结构的优化算法,由于 Dw 卷积的各个通道是独立计算的,我们选择将 Conv1x1_1 与 DwConv3x3 进行融合,在 Conv1x1_1 的部分结果计算出来之后立即对 DwConv3x3 对应通道的结果进行计算,使 Dw 卷积的计算完全在 L2 Cache 中计算完毕,几乎消除了数据在 L3 和内存上的来回传输,根据不同的参数,对 Conv1x1_1 与 DwConv3x3 的加速比达到 4 倍以上。

 

 

△ Conv1x1_1 与 DwConv3x3 融合示意图

 

ChannelShuffle 融合

 

ChannelShuffle 是一种在 ShuffleNet 系列网络中引入的结构,和 MobileNet 一样,ShuffleNet 也是一种为移动端和嵌入式的低算力设备设计的网络,随着应用范围的扩大在不同平台上也遇到了类似的访存受限的情况。

 

为此 OpenPPL 也针对 ChannelShuffle 做了一定的融合优化,具体可参考 :point_down|type_1_2:

 

《小优化大提升:针对 ShuffleNet 的图优化》

 

内存传送 / 就地计算

 

在推理计算涉及的算子中,除了卷积和矩阵乘之外,大部分算子都是访存受限的,当数据量比较大,导致网络中间数据超出 Cache 大小的时候,也会对推理性能造成一定的影响,甚至可能因为占用过大的 Cache 空间影响系统中其他计算任务的发挥。

 

同时,也有一部分算子是只对 Tensor 的形状进行修改但是不进行任何计算的,比如 Reshape , Squeeze , Unsqueeze 等,这种算子我们也应该尽量避免进行没有意义的内存搬运和内存空间占用。

 

针对以上两种算子, OpenPPL 引入了内存传送特性,允许开发者手动将一个 Tensor 的内存地址移权给另一个 Tensor 中。 对于第一类访存瓶颈的算子,我们直接在输入 Tensor 上进行就地计算,然后将输入 Tensor 的内存移权到输出 Tensor 上,不只是 ReLU , Reorder 这样的单个输入和输出的算子, Add 、 Sub 这一类的双输入算子也可以利用这个特性去复用其中一个输入 Tensor 的内存。 对于第二类( Reshape 类)算子,我们直接移权内存地址就可以完成优化。

 

 

SSE / 国产芯片支持

 

OpenPPL  X86 为特别支持国产芯片和一些老架构的 Intel 平台的处理器,以及一些嵌入式 X86 平台的运行,引入了 SSE 指令集的支持,同时也进行了一定程度的优化。

 

目前可以在国产海光、兆芯、 Intel nehelem/westmere 架构的服务器 CPU,或者 Apollo Lake 等嵌入式处理器 上跑通模型,其中在兆芯上的推理性能也相对可 观。

 

Windows 编译支持

 

OpenPPL X86 响应社区需求兼容了 Windows 工具链的编译支持,可以兼容 VS2015~VS2019 。

 

性能展示

 

以下是部分  benchmark 数据,更多 benchmark 数据可以在官方 repo 查看:

 

:link:  https://github.com/openppl-public/ppl.nn/blob/master/docs/en/x86-doc/benchmark_tool.md

 

:star:️ 欢迎 star

 

:link:  https://github.co m/openppl-public

 

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。