Skylake是否需要vzerooper让turbo时钟在512位指令后恢复,该指令只读取ZMM寄存器,写入k掩码



写入ZMM寄存器可以使Skylake-X(或类似)CPU无限期地处于最大turbo减少的状态。(SIMD指令降低CPU频率并动态确定流氓AVX-512指令的执行位置)Ice Lake大概也类似。

(解决方法:根据@BeeOnRope的评论,zmm16..31没有问题,我在《如果您的程序+库不包含SSE指令,使用VZEROUPER有用吗?所以这个strlen可以只使用vpxord xmm16,xmm16,xmm16vpcmpeqb与zmm16。)

如果您有硬件,如何测试:

@BeeOnRope在RWT线程中发布了测试代码:将vbroadcastsd zmm15, [zero_dp]替换为vpcmpeqb k0, zmm0, [rdi]作为"dirtieing"指令,并查看之后的循环运行得是慢还是快。


我假设执行任何512位uop都会暂时触发reduced turbo(同时关闭矢量ALU uop的端口1,而512位uop实际上在后端),但问题是:如果在读取ZMM寄存器后从未使用vzeroupper,CPU会自行恢复吗?

(和/或以后的SSE或AVX指令会有转换惩罚或错误依赖性吗?)

具体来说,像这样使用insns的strlen在返回之前是否需要vzeroupper(在任何实际CPU上的实践中,和/或英特尔为未来证明的最佳实践而记录的。)假设后面的指令可能包括非VEX SSE和/或VEX编码的AVX1/2,而不仅仅是GP整数,以防这与保持turbo减少的dirty-upper-256情况有关。

; check 64 bytes for zero, strlen building block.
vpxor     xmm0,xmm0,xmm0    ; zmm0 = 0 using AVX1 implicit zero-extension
vpcmpeqb  k0, zmm0, [rdi]   ; 512-bit load + ALU, not micro-fused
;kortestq k0,k0 / jnz or whatever
kmovq     rax, k0
tzcnt     rax, rax
;vzeroupper  before lots of code that goes a long time before another 512-bit uop?

(受AVX512BW中strlen的启发:使用bsf/tzcnt?处理32位代码中的64位掩码,如果对其矢量reg进行适当优化以使用较短的VEX而不是EVEX指令,则会是这样。)


关键指令是vpcmpeqb k0, zmm0, [rdi],它在SKX或CNL上解码为2个独立的uop(非微融合:引退插槽=2.0):512位加载(到512位物理寄存器?)和ALU比较到掩码寄存器。

但是,没有体系结构ZMM寄存器被显式写入,只有读取。因此,假设至少有一个xsave/xrstor会清除任何"脏上限"条件,如果在这之后存在的话。(除非在Linux内核上有一个实际的上下文切换到不同的用户空间进程,或者线程迁移,否则这种情况不会在Linux上发生;仅仅进入内核进行中断不会导致这种情况。因此,如果你有硬件,这实际上在主流操作系统下仍然是可以测试的;我没有。)

我可以想象SKX/CNL和/或冰湖的可能性:

  • 没有长期影响:max turbo恢复得和vzeroupper一样快
  • 在上下文切换之前,最大turbo限制为512位速度。(由于体系结构规则是干净的,所以xrstor或等效的清除任何脏的上部状态标志)
  • 即使在上下文切换中,最大turbo速度也限制为512位,就像运行vaddps zmm0,zmm0,zmm0一样。(Dirty upper标志设置在已保存和已恢复的体系结构状态中。)这很合理,因为xsaveopt确实会跳过保存向量regs的128或256的高位,如果已知它们是干净的

我认为kmovq不会降低最大涡轮增压或触发任何其他512位uop效果。掩码寄存器的高32位通常只与64字节矢量的AVX512BW一起使用,但据推测,它们不会单独对掩码寄存器的前32位进行通电,只有矢量寄存器的前32字节。有一些用例,如使用kshiftkunpack来处理64位掩码块(用于加载/存储或传输到整数regs),即使您在使用带有YMM或XMM regs的AVX512VL时一次只生成或使用它们32位。


PS:Xeon Phi不受这些影响;当运行其他代码时,它并没有被构建为在繁重的AVX512之后进行上时钟,因为它是为运行AVX512而设计的。事实上,vzeroupper非常慢,不建议在KNL/KNM上使用。

我的示例使用AVX512BW这一事实实际上与这个问题无关,但所有使用AVX512的主流(而非Xeon Phi)CPU都具有AVX512BW。它只是一个很好的真实用例,使用AVX512BW排除KNL这一事实无关紧要。

,如果使用zmm寄存器作为比较器之一,至少在SKX上,将vpcmpeqb放入掩码寄存器不会触发慢速模式。

读取键512位寄存器(键寄存器为zmm0-zmm15)的任何其他指令(就我所测试的而言)也是如此。例如,vpxord zmm16, zmm0, zmm1也不会弄脏鞋面,因为当它涉及作为密钥寄存器的zmm1zmm0时,它只从它们读取,而写入不是密钥寄存器的zmm16

我在Xeon W-2104上使用avx turbo进行了测试,它的标称速度为3.2 GHz,L1 turbo许可证(AVX2 turbo)为2.8 GHz,L2许可证(avx-512 turbo)为2.4 GHz。在每次使用vpxord zmm15, zmm14, zmm15进行测试之前,我使用--dirty-upper选项来弄脏鞋面。这导致任何使用任何SIMD寄存器(包括标量SSE FP)的测试都以较慢的2.8 GHz速度运行,如以下结果所示(查看A/M-MHz列中的cpu频率):

CPUID highest leaf  : [16h]
Running as root     : [YES]
MSR reads supported : [YES]
CPU pinning enabled : [YES]
CPU supports AVX2   : [YES]
CPU supports AVX-512: [YES]
cpuid = eax = 2, ebx = 266, ecx = 0, edx = 0
cpu: family = 6, model = 85, stepping = 4
tsc_freq = 3191.8 MHz (from calibration loop)
CPU brand string: Intel(R) Xeon(R) W-2104 CPU @ 3.20GHz
4 available CPUs: [0, 1, 2, 3]
4 physical cores: [0, 1, 2, 3]
Will test up to 1 CPUs
Cores | ID                  | Description                     | OVRLP1 | OVRLP2 | OVRLP3 | Mops | A/M-ratio | A/M-MHz | M/tsc-ratio
1     | pause_only          | pause instruction               |  1.000 |  1.000 | 1.000  | 2256 |      0.99 |    3173 | 1.00       
1     | ucomis_clean        | scalar ucomis (w/ vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | ucomis_dirty        | scalar ucomis (no vzeroupper)   |  1.000 |  1.000 | 1.000  |  466 |      0.88 |    2793 | 1.00       
1     | scalar_iadd         | Scalar integer adds             |  1.000 |  1.000 | 1.000  | 3192 |      0.99 |    3165 | 1.00       
1     | avx128_iadd         | 128-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_iadd         | 256-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 2793 |      0.87 |    2793 | 1.00       
1     | avx512_iadd         | 512-bit integer adds            |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_iadd_t       | 128-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 8380 |      0.88 |    2793 | 1.00       
1     | avx256_iadd_t       | 256-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 8380 |      0.88 |    2793 | 1.00       
1     | avx128_mov_sparse   | 128-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_mov_sparse   | 256-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_mov_sparse   | 512-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2794 |      0.87 |    2793 | 1.00       
1     | avx128_merge_sparse | 128-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_merge_sparse | 256-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_merge_sparse | 512-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift       | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_vshift       | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_vshift       | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift_t     | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 5587 |      0.88 |    2793 | 1.00       
1     | avx256_vshift_t     | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 5588 |      0.88 |    2793 | 1.00       
1     | avx512_vshift_t     | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_imul         | 128-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx256_imul         | 256-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx512_imul         | 512-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx128_fma_sparse   | 128-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx256_fma_sparse   | 256-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx512_fma_sparse   | 512-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_fma          | 128-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx256_fma          | 256-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.87 |    2793 | 1.00       
1     | avx512_fma          | 512-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx128_fma_t        | 128-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 4789 |      0.75 |    2394 | 1.00       
1     | avx256_fma_t        | 256-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 4790 |      0.75 |    2394 | 1.00       
1     | avx512_fma_t        | 512-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 2394 |      0.75 |    2394 | 1.00       
1     | avx512_vpermw       | 512-bit serial WORD permute     |  1.000 |  1.000 | 1.000  |  466 |      0.88 |    2793 | 1.00       
1     | avx512_vpermw_t     | 512-bit parallel WORD permute   |  1.000 |  1.000 | 1.000  | 1397 |      0.87 |    2793 | 1.00       
1     | avx512_vpermd       | 512-bit serial DWORD permute    |  1.000 |  1.000 | 1.000  |  931 |      0.87 |    2793 | 1.00       
1     | avx512_vpermd_t     | 512-bit parallel DWORD permute  |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       

唯一全速运行的测试是Scalar integer adds,它根本没有SSE/AVX寄存器的使用,而scalar ucomis (w/ vzeroupper)在每次测试之前都有一个显式的vzeroupper,因此不会使用脏鞋面执行。

然后,我将dirtieing指令更改为您感兴趣的vpcmpeqb k0, zmm0, [rsp]指令

Cores | ID                  | Description                     | OVRLP1 | OVRLP2 | OVRLP3 | Mops | A/M-ratio | A/M-MHz | M/tsc-ratio
1     | pause_only          | pause instruction               |  1.000 |  1.000 | 1.000  | 2256 |      1.00 |    3192 | 1.00       
1     | ucomis_clean        | scalar ucomis (w/ vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | ucomis_dirty        | scalar ucomis (no vzeroupper)   |  1.000 |  1.000 | 1.000  |  790 |      1.00 |    3192 | 1.00       
1     | scalar_iadd         | Scalar integer adds             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx128_iadd         | 128-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3190 | 1.00       
1     | avx256_iadd         | 256-bit integer serial adds     |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_iadd         | 512-bit integer adds            |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_iadd_t       | 128-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 9575 |      1.00 |    3192 | 1.00       
1     | avx256_iadd_t       | 256-bit integer parallel adds   |  1.000 |  1.000 | 1.000  | 9577 |      1.00 |    3192 | 1.00       
1     | avx128_mov_sparse   | 128-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_mov_sparse   | 256-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_mov_sparse   | 512-bit reg-reg mov             |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_merge_sparse | 128-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_merge_sparse | 256-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_merge_sparse | 512-bit reg-reg merge mov       |  1.000 |  1.000 | 1.000  | 2793 |      0.88 |    2793 | 1.00       
1     | avx128_vshift       | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_vshift       | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_vshift       | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_vshift_t     | 128-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 6386 |      1.00 |    3192 | 1.00       
1     | avx256_vshift_t     | 256-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 6386 |      1.00 |    3192 | 1.00       
1     | avx512_vshift_t     | 512-bit variable shift (vpsrld) |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00       
1     | avx128_imul         | 128-bit integer muls            |  1.000 |  1.000 | 1.000  |  638 |      1.00 |    3192 | 1.00       
1     | avx256_imul         | 256-bit integer muls            |  1.000 |  1.000 | 1.000  |  639 |      1.00 |    3192 | 1.00       
1     | avx512_imul         | 512-bit integer muls            |  1.000 |  1.000 | 1.000  |  559 |      0.88 |    2793 | 1.00       
1     | avx128_fma_sparse   | 128-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx256_fma_sparse   | 256-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 3193 |      1.00 |    3192 | 1.00       
1     | avx512_fma_sparse   | 512-bit 64-bit sparse FMAs      |  1.000 |  1.000 | 1.000  | 2793 |      0.87 |    2793 | 1.00       
1     | avx128_fma          | 128-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  798 |      1.00 |    3192 | 1.00       
1     | avx256_fma          | 256-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  798 |      1.00 |    3192 | 1.00       
1     | avx512_fma          | 512-bit serial DP FMAs          |  1.000 |  1.000 | 1.000  |  698 |      0.88 |    2793 | 1.00       
1     | avx128_fma_t        | 128-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 6384 |      1.00 |    3192 | 1.00       
1     | avx256_fma_t        | 256-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 5587 |      0.87 |    2793 | 1.00       
1     | avx512_fma_t        | 512-bit parallel DP FMAs        |  1.000 |  1.000 | 1.000  | 2394 |      0.75 |    2394 | 1.00       
1     | avx512_vpermw       | 512-bit serial WORD permute     |  1.000 |  1.000 | 1.000  |  466 |      0.87 |    2793 | 1.00       
1     | avx512_vpermw_t     | 512-bit parallel WORD permute   |  1.000 |  1.000 | 1.000  | 1397 |      0.88 |    2793 | 1.00       
1     | avx512_vpermd       | 512-bit serial DWORD permute    |  1.000 |  1.000 | 1.000  |  931 |      0.88 |    2793 | 1.00       
1     | avx512_vpermd_t     | 512-bit parallel DWORD permute  |  1.000 |  1.000 | 1.000  | 2794 |      0.88 |    2793 | 1.00     

现在大多数测试都在全速运行。仍在2.8GHz(或者在一种情况下,对于并行512位FMA为2.4GHz)下运行的是那些实际使用512位矢量,或者使用256位矢量和重FP指令(如FMA)的指令。

最新更新