什么时候使用AVX512指令集
处理器处理数据都需要使用寄存器。64位的处理器经常使用64位的寄存器。大部分现代处理器有向量化的指令,这些指令使用更大的寄存器,比如128bit(sse指令集)、256bit(avx/avx2指令集)、512bit(avx512指令集)。Intel新的处理器都支持512bit的寄存器,使用这些更大的寄存器会对程序运算有明显的加速,因为它们可以一次计算256bit寄存器的2倍数据。
但是,使用这些更长的寄存器指令会让处理器需要开启更大的功率产生更多的热量,所以有可能导致处理器降频,反而让处理器运算速度变慢。为了让功率保持在合理的区间,Intel会让处理器核心动态调整频率,所以当处理器功率太高产生的热量太大,处理器的频率就会降低,从而减少热量的产生,进行降温防止损坏处理器。当更长bit的寄存器在使用的时候,会适度减少处理器的频率以降低功耗,尤其是当处理器连续执行这些重量级的指令时。最差的情况是可能处理器的频率只有正常情况下的一半,最后导致整体程序运行速度反而变慢。
所以,有的工程师建议应该把服务器的AVX512指令支持默认关闭。
所以到底应该怎么做呢?
AVX512指令可以使用不同长度的寄存器(128bit、256bit和512bit)。当讨论AVX-512时,就是表明我们在使用512bit的寄存器。因此如果你使用的是128bit和256bit的寄存器,不用担心降频,因为不是运行的AVX-512。尤其是使用128bit的寄存器的时候更不必担心。
每个核心在使用特定指令后短时间(e.g., ~2ms)内会发生降频。
指令会分为重量级指令和轻量级指令。一般涉及到float运算或者整数乘法运算(会执行在浮点数运算单元)就是重量级指令。除了乘法操作的整数操作,逻辑运算,数据shuffling(vpermw,vpermd)等等是轻量级操作。在深度学习中重量级操作有数值分析高性能计算以及加密运算。轻量级指令更倾向于文本处理,快速压缩任务以及一些向量化的库函数(System.arrayCopy -> java)。
Intel的cpu运行有3种模式:(1)L0:最快的模式,turbo 频率。(2)L1:比L0慢。(3)L2:最慢的模式。如果要达到L2模式,你需要连续执行重量级的AVX-512指令,连续指的是每个指令周期都是执行的重量级指令。同样,如果你连续使用256bit的寄存器执行重量级指令,你肯能会达到L1模式。处理器并不会在你执行重量级指令时立即进入数字更高的模式,它会首先以比较低的性能执行这些指令(比如4x的慢速),当处理器发现还有更多的指令需要执行时才会改变频率。否则其他512bit的指令将会以L1的模式执行(处理器会立即停止并改变频率当遇到更多的其他的指令)。在服务器cpu中,是否降频取决于每一个相同socket下的cpu的L级别和活跃的核心数量。所以判断核心是否降频,仅仅需要知道它的L级数以及活跃的核心数量。因此,除了使用超线程时的同级逻辑核心外,我们无法降低同一插槽上的其他核心的时钟频率,只能通过在一个核心上持续的运行 AVX-512 指令来实现整体cpu降频。当然,如果我们将重量级的任务分布在了个别核心上,将其他核心与这个任务分割开,降频就会仅仅发生在这些核心上。在linux上,可以使用
taskset
或者numactl
来控制任务运行在哪些核心上。可以在网上看见这个表格 online:mode 1 active core 9 active cores Normal 3.2 GHz 2.7 GHz AVX2 3.1 GHz 2.3 GHz AVX-512 2.9 GHz 1.6 GHz 这个表格仅包含两列。AVX-512指令下,9-12个活动核心时都是最坏的情况(L2)。这个表格有一些误导人,这个频率指的是连续执行指令时才会按照表格降频。
这个表格有一些有用的信息:
a. 并不是avx-512才会带来降频,如果有很多的活动核心,也会降频严重。
b. 如果仅仅使用轻量级的AVX-512指令,就算使用了全部核心,降频并不严重(15%)。
c. 如果连续执行重量级的指令,降频将会很严重。(40%)
所以,我们的任务并不是最大化cpu的执行频率,而是加速程序运行。所以我们要根据cpu运行规律做一些工程上的经验总结,来决定什么时候使用avx512来获得最大收益。
所以经验总结为:
- 工程师应该使用频率检测工具来确保任务跑在预期的cpu L级别模式上。很多降频很容易发现,比如使用
perf stat
命令来来决定处理器的平均频率,使用 CORE_POWER.LVL0_TURBO_LICENSE 事件(以及 LVL1 和 LVL2 的相同事件)可以获得更细粒度的细节。 - 在标准的台式机上,不必太担心像服务器芯片中这样的降频。像Intel XEON W-2104处理器,最差的降频是3.2-2.4Ghz。25%的降频并不是一个太大的风险。
- 如果代码需要连续运行重量级指令,可以考虑将任务划分给特定的核心,限制更多活动核心带来的降频。或者考虑将AVX-512指令和其他指令混合执行。需要确保使用AVX-512能够带来明显的好处,比如每一个指令周期是两倍的性能好处。如果AVX-512的代码比不是512的代码仅快了30%,那么如果任务运行在全部核心上时并不能带来什么性能提升。例如,openssl 项目使用繁重的 AVX-512 指令将特定散列算法 (poly1305) 的成本从每字节 0.51 个周期(使用 256 位 AVX 指令时)降低到每字节 0.35 个周期,提高了 30% 每个周期的基础上。 他们已经禁用了此优化。
- 轻量级的 AVX-512 的门槛较低。 即使任务分布在所有内核上,我们在 Xeon Gold 等某些芯片上也可能只能获得 15% 的频率降低。 因此,我们只需检查 AVX-512 在每个周期的基础上为我们的整体应用程序带来超过 15% 的增益。
- 性能库提供者可能应该让库用户来决定 AVX-512 是否值得使用。 例如,可以提供编译时选项以启用或禁用 AVX-512 功能,甚至提供运行时选择。 对性能敏感的库应该记录他们采用的方法以及更广泛的指令可能带来的加速。
- 编译器可能会在我们的代码中插入了 AVX-512 指令。 即使我们没有使用任何显式 AVX-512 指令或内在函数,编译器也可能会决定在库函数和其他优化中使用它们作为循环矢量化的结果。 即使像复制struct这样简单的操作也可能导致 AVX-512 指令出现在我们的程序中。 当前的编译器行为差异很大,我们可以预期它在未来会发生变化。 事实上,它已经发生了变化:英特尔在早期版本的 icc 编译器中更积极地使用 AVX-512 指令,但此后删除了大部分这种激进的行为,除非用户使用特殊的命令行选项要求它。 对LLVM 的 clang(macOS 上的默认编译器)、GNU gcc、Intel 的编译器(icc)和 MSVC(Microsoft Visual Studio 的一部分)的综合测试,只有 clang 在积极使用 512 位指令:它在复制结构、内联 memcpy 和向量化循环时使用AVX-512指令。 英特尔 icc 编译器和 gcc 似乎只为这个测试生成 AVX-512 指令,使用非默认参数:-qopt-zmm-usage=high 用于 icc,-mprefer-vector-width=512 用于 gcc。 事实上,对于大多数代码,例如生成的副本,gcc 似乎更喜欢使用 128 位寄存器而不是 256 位寄存器。 MSVC 目前(直到 2017 版)根本不支持编译器生成的 AVX-512 使用,尽管它确实支持通过标准内部函数使用 AVX-512。 从编译器的角度来看,决定使用 AVX-512 指令很困难:它们通常提供合理的局部加速,但可能会降低整个内核的速度。 如果此类指令的频率足以使内核在 L1 许可证中运行,但不足以产生足够的加速来抵消频率导致的速度减慢,则在重新编译以支持 AVX-512 后,程序的整体运行速度可能会变慢。 除了使用和不使用 AVX-512 编译我们的程序以及在现实环境中进行基准测试以确定哪个更快之外,这里很难给出一般性的建议。
reference:
AVX-512: when and how to use these new instructions – Daniel Lemire’s blog