问题描述
写入ZMM寄存器可以使Skylake-X(或类似的)CPU无限期地处于最大涡流降低的状态. ( SIMD指令可降低CPU频率和),大概Ice Lake是类似的./p>根据我在 因此,这个strlen只能将vpxord xmm16,xmm16,xmm16
和vpcmpeqb
与zmm16一起使用.)
如果有硬件,如何进行测试:
@BeeOnRope发布了在RWT线程中测试代码:将vbroadcastsd zmm15, [zero_dp]
替换为vpcmpeqb k0, zmm0, [rdi]
作为脏污"指令,然后查看之后的循环是慢速还是快速运行.
我假设执行任何512位uop都会暂时触发减少的turbo(以及在512位uop实际上在后端时关闭矢量ALU uops的端口1),但是问题是:CPU可以吗?如果您只是在读取 ZMM寄存器后就从未使用过vzeroupper
,是否可以自行恢复?
(和/或以后的SSE或AVX指令将具有过渡惩罚或错误的依赖关系吗?)
具体来说,使用这样的insns的strlen
在返回之前是否需要vzeroupper
?(实际上在任何实际CPU上,和/或如英特尔所记录的那样,都是面向未来的最佳实践) .)假设以后的指令可能包括非VEX SSE和/或VEX编码的AVX1/2,而不仅仅是GP整数,以防与上256脏情况保持Turbo减小有关.
; 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:使用bsf/tzcnt处理32位代码中的64位掩码吗?如果将其向量reg调零正确优化以使用较短的VEX而不是EVEX指令,则将看起来像这样.)
关键指令是vpcmpeqb k0, zmm0, [rdi]
,它在SKX或CNL上解码为2个单独的uops(不是微融合的:retire-slots = 2.0 ):512位负载(到512位物理寄存器中?),而ALU与掩码寄存器进行比较.
但是没有任何 architectureural ZMM寄存器被显式写入,只能读取.因此,假设至少有一个xsave
/xrstor
可以清除任何脏污上限"条件(如果此条件之后存在). (在Linux上不会发生这种情况,除非在该内核上有实际的上下文切换到另一个用户空间进程,或者线程迁移了;仅进入内核进行中断不会导致它.因此,这实际上仍然可以在主流操作系统,如果您有硬件,我没有.)
我可以想象的SKX/CNL和/或Ice Lake的可能性:
- 没有长期影响:max-turbo的恢复速度与
vzeroupper
一样快 - 最大速度限制为512位,直到上下文切换为止. (
xrstor
或同等功能会清除所有肮脏的上层状态标志,因为体系结构规则是干净的.) - 即使在上下文切换之间,最大加速限制为512位速度,就像运行
vaddps zmm0,zmm0,zmm0
一样. (肮脏的上层标志设置为已保存,并以体系结构状态还原.)这很合理,因为xsaveopt
确实跳过了保存的矢量regs的高128或256,如果知道它们是干净的,则会跳过.
我认为kmovq
不会降低最大加速或触发任何其他512位uop效果.掩码寄存器的高32位通常仅与64字节向量的AVX512BW一起使用,但大概它们不会单独对掩码寄存器的高32位单独进行功率门控,只有高32位 矢量regs.有一些使用案例,例如使用kshift
或kunpack
处理64位掩码块(用于加载/存储或传输到整数reg),即使使用AVX512VL一次仅生成或使用32位掩码也是如此使用YMM或XMM规则.
PS:至强融核不受这些影响.在运行其他代码时,它不是要在不超过重型AVX512的情况下向上运行,因为它是为运行AVX512而设计的.实际上vzeroupper
速度非常慢,因此不建议在KNL/KNM上使用.
我的示例使用AVX512BW的事实实际上与问题无关,但是所有带有AVX512的主流(非Xeon Phi)CPU都具有AVX512BW.它只是一个很好的实际用例,使用AVX512BW排除KNL的事实是无关紧要的.
否,如果将zmm
寄存器用作掩码寄存器之一,则进入掩码寄存器的vpcmpeqb
不会触发慢速模式比较起来,至少在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.在使用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
全速运行的唯一测试是完全不使用SSE/AVX寄存器的Scalar integer adds
和在每次测试之前均具有显式vzeroupper
的scalar ucomis (w/ vzeroupper)
,因此不会在脏鞋帮上执行./p>
然后,我将弄脏指令更改为您感兴趣的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.8 GHz上运行(对于并行512位FMA,一种情况下仍为2.4 GHz)的是那些实际使用512位矢量,或使用256位矢量和较重的FP指令(如FMA)的设备,如预期的那样.
Writing a ZMM register can leave a Skylake-X (or similar) CPU in a state of reduced max-turbo indefinitely. (SIMD instructions lowering CPU frequency and Dynamically determining where a rogue AVX-512 instruction is executing) Presumably Ice Lake is similar.
(Workaround: not a problem for zmm16..31, according to @BeeOnRope's comments which I quoted in Is it useful to use VZEROUPPER if your program+libraries contain no SSE instructions? So this strlen could just use vpxord xmm16,xmm16,xmm16
and vpcmpeqb
with zmm16.)
How to test this if you have hardware:
@BeeOnRope posted test code in an RWT thread: replace vbroadcastsd zmm15, [zero_dp]
with vpcmpeqb k0, zmm0, [rdi]
as the "dirtying" instruction and see if the loop after that runs slow or fast.
I assume executing any 512-bit uop will trigger reduced turbo temporarily (along with shutting down port 1 for vector ALU uops while the 512-bit uop is actually in the back-end), but the question is: Will the CPU recover on its own if you never use vzeroupper
after just reading a ZMM register?
(And/or will later SSE or AVX instructions have transition penalties or false dependencies?)
Specifically, does a strlen
using insns like this need a vzeroupper
before returning? (In practice on any real CPU, and/or as documented by Intel for future-proof best practices.) Assume that later instructions may include non-VEX SSE and/or VEX-encoded AVX1/2, not just GP integer, in case that's relevant to a dirty-upper-256 situation keeping turbo reduced.
; 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?
(Inspired by the strlen in AVX512BW: handle 64-bit mask in 32-bit code with bsf / tzcnt? which would look like this if zeroing its vector reg was properly optimized to use a shorter VEX instead of EVEX instruction.)
The key instruction is the vpcmpeqb k0, zmm0, [rdi]
which decodes on SKX or CNL to 2 separate uops (not micro-fused: retire-slots = 2.0): a 512-bit load (into a 512-bit physical register?) and an ALU compare into a mask register.
But no architectural ZMM register is ever written explicitly, only read. So presumably at least an xsave
/xrstor
would clear any "dirty upper" condition, if one exists after this. (This won't happen on Linux unless there's an actual context switch to a different user-space process on that core, or the thread migrates; merely entering the kernel for interrupts won't cause it. So this is actually still testable under a mainstream OS, if you have the hardware; I don't.)
Possibilities I can imagine for SKX/CNL, and/or Ice Lake:
- No long-term effect: max-turbo recovers just as quickly as with
vzeroupper
- Max turbo limited to 512-bit speed until a context switch. (
xrstor
or equivalent clears any dirty-upper state flag because the architectural regs are clean). - Max turbo limited to 512-bit speed even across context switches, just like if you'd run
vaddps zmm0,zmm0,zmm0
. (Dirty upper flag is set in the saved and restored with the architectural state.) Plausible becausexsaveopt
does skip saving the upper 128 or 256 of vector regs if it's known they're clean.
I assume kmovq
won't reduce max turbo or trigger any of the other 512-bit uop effects. The upper 32 bits of mask registers normally only come into play with with AVX512BW for 64-byte vectors, but presumably they don't power-gate the top 32 bits of mask regs separately, only the top 32 bytes of vector regs. There are use-cases like using kshift
or kunpack
to deal with 64-bit chunks of masks (for load/store or transfer to integer regs) even if you only ever generate or use them 32 bits at a time with AVX512VL with YMM or XMM regs.
PS: Xeon Phi is not subject to these effects; it's not built to upclock beyond heavy AVX512 when running other code because it's made to run AVX512. And in fact vzeroupper
is very slow and not recommended on KNL / KNM.
The fact that my example uses AVX512BW is really not relevant to the question, but all mainstream (not Xeon Phi) CPUs with AVX512 have AVX512BW. It just makes a nice real use-case, and the fact that using AVX512BW excludes KNL is irrelevant.
No, a vpcmpeqb
into a mask register does not trigger slow mode if you use a zmm
register as one of the comparands, at least on SKX.
This is also true of any of any other instruction (as far as I tested) which only reads the key 512-bit registers (the key registers being zmm0
- zmm15
). For example, vpxord zmm16, zmm0, zmm1
also does not dirty the uppers because while it involves zmm1
and zmm0
which are key registers, it only reads from them while writing zmm16
which is not a key register.
I tested this using avx-turbo on a Xeon W-2104, which has a nominal speed of 3.2 GHz, L1 turbo license (AVX2 turbo) of 2.8 GHz, and a L2 license (AVX-512 turbo) of 2.4 GHz. I used the --dirty-upper
option to dirty the uppers before each test with vpxord zmm15, zmm14, zmm15
. This causes any test that uses any SIMD registers at all (including scalar SSE FP) to run at the slower 2.8 GHz speed, as shown in these results (look at the A/M-MHz column for cpu frequency):
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
The only tests that ran at full speed were Scalar integer adds
which has no SSE/AVX register use at all, and scalar ucomis (w/ vzeroupper)
which has an explicit vzeroupper
before each test so doesn't execute with dirty uppers.
Then, I changed the dirtying instruction to the vpcmpeqb k0, zmm0, [rsp]
instruction you are interested in. The new results:
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
Most tests now run at full speed. The ones still running at 2.8 GHz (or in one case 2.4 GHz for parallel 512-bit FMAs) are those which actually use 512-bit vectors, or use 256-bit vectors and heavy FP instructions like FMA, as expected.
这篇关于在仅读取ZMM寄存器并写入k掩码的512位指令之后,Skylake是否需要vzeroupper来使turbo时钟恢复?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!