Pet*_*des 5 x86 assembly intel micro-optimization avx512
编写ZMM寄存器可以使Skylake-X(或类似的)CPU无限期地处于最大涡流降低的状态。(SIMD指令可降低CPU频率并动态确定恶意AVX-512指令在何处执行)推测Ice Lake是类似的。
(解决方法:zmm16..31不是问题,据@ BeeOnRope的意见,我在报?是有用的,如果你的程序+库不包含SSE指令使用VZEROUPPER
所以这strlen的可以只使用vpxord xmm16,xmm16,xmm16和vpcmpeqb,与zmm16)
@BeeOnRope发布测试代码在RWT线:更换vbroadcastsd zmm15, [zero_dp]同vpcmpeqb k0, zmm0, [rdi]为“弄脏”指令,看看是否能运行后循环慢或快。
我假设执行任何512位uop都会暂时触发减少的turbo(同时关闭向量ALU uops的端口1,而512位uop实际上在后端),但问题是:CPU能否在其上恢复如果您vzeroupper仅在读取 ZMM寄存器后就从未使用过,您是否拥有?
(和/或以后的SSE或AVX指令是否会有过渡惩罚或错误的依赖关系?)
具体来说,这样的strlen使用insns vzeroupper在返回之前是否需要a ? (实际上,在任何实际的CPU上,和/或Intel记录的有关面向未来的最佳实践。)假定以后的指令可能包括非VEX SSE和/或VEX编码的AVX1 / 2,而不仅仅是GP整数,以防万一。这与使turbo减少的上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?
Run Code Online (Sandbox Code Playgroud)
(受AVX512BW的强烈启发:使用bsf / tzcnt?处理32位代码中的64位掩码?如果将其向量reg调零正确优化以使用较短的VEX而不是EVEX指令,则将看起来像这样。)
关键指令是vpcmpeqb k0, zmm0, [rdi]在SKX或CNL上将其解码为2个单独的uops(不是微融合的:retire-slots = 2.0):512位负载(到512位物理寄存器中?)和ALU比较成掩码寄存器。
但是,没有任何架构性的 ZMM寄存器曾经被明确写入,只能被读取。因此,假设至少有一个xsave/ xrstor可以清除任何“肮脏的上限”条件,如果此条件之后存在的话。(除非在该内核上有实际的上下文切换到不同的用户空间进程,或者线程迁移,否则这不会在Linux上发生;仅进入内核进行中断不会导致它。因此,这实际上仍然可以在主流操作系统(如果您拥有硬件;我没有)。
我可以想象SKX / CNL和/或Ice Lake的可能性:
vzeroupperxrstor或等效清除所有脏的上层状态标志,因为体系结构规则是干净的)。vaddps zmm0,zmm0,zmm0。(在保存的状态下设置了肮脏的上限标志,并以体系结构状态进行了恢复。)合理,因为xsaveopt如果知道它们是干净的,则跳过保存上限128或256个向量reg。我认为kmovq不会降低最大加速或触发其他512位uop效果。掩码寄存器的高32位通常仅与用于64字节向量的AVX512BW一起使用,但是大概它们不会单独对掩码寄存器的高32位(仅是向量寄存器的高32 字节)进行电源门控。在某些情况下,即使使用带有YMM或XMM regs的AVX512VL仅一次生成或使用32位掩码,也可以使用kshift或kunpack处理64位掩码(用于加载/存储或传输到整数regs)。
PS:至强融核不受这些影响。在运行其他代码时,它不是要在不超过重型AVX512的情况下向上运行,因为它是为运行AVX512而设计的。而且实际上vzeroupper非常慢,因此不建议在KNL / KNM上使用。
我的示例使用AVX512BW的事实实际上与问题无关,但是所有带有AVX512的主流(非Xeon Phi)CPU都具有AVX512BW。它只是一个很好的实际用例,与使用AVX512BW排除KNL无关。
不,vpcmpeqb如果您使用zmm寄存器作为比较器之一,则 a into a mask register 不会触发慢速模式,至少在 SKX 上。
对于仅读取关键 512 位寄存器(关键寄存器是zmm0- zmm15)的任何其他指令(据我测试)也是如此。例如,vpxord zmm16, zmm0, zmm1也不会弄脏上部,因为虽然它涉及zmm1和zmm0哪些是关键寄存器,但它只在写入时从它们读取而zmm16不是关键寄存器。
我在 Xeon W-2104 上使用avx-turbo对此进行了测试,其标称速度为 3.2 GHz,L1 turbo 许可 (AVX2 turbo) 为 2.8 GHz,L2 许可 (AVX-512 turbo) 为 2.4 GHz。我使用了--dirty-upper在每次测试前用vpxord zmm15, zmm14, zmm15. 这会导致任何使用任何 SIMD 寄存器的测试(包括标量 SSE FP)以较慢的 2.8 GHz 速度运行,如以下结果所示(查看 CPU 频率的 A/M-MHz 列):
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
Run Code Online (Sandbox Code Playgroud)
唯一全速运行的测试Scalar integer adds根本没有使用 SSE/AVX 寄存器,并且在每次测试之前scalar ucomis (w/ vzeroupper)都有一个显式的vzeroupper测试,因此不会在脏鞋帮上执行。
然后,我把dirtying指令改成了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
Run Code Online (Sandbox Code Playgroud)
大多数测试现在全速运行。那些仍然以 2.8 GHz 运行(或者在一种情况下,并行 512 位 FMA 运行在 2.4 GHz)的是那些实际使用 512 位向量的那些,或者使用 256 位向量和像 FMA 这样的重 FP 指令,正如预期的那样。
| 归档时间: |
|
| 查看次数: |
109 次 |
| 最近记录: |