Skylake 是否需要 vzeroupper 才能在仅读取 ZMM 寄存器并写入 k 掩码的 512 位指令后恢复涡轮时钟?
Does Skylake need vzeroupper for turbo clocks to recover after a 512-bit instruction that only reads a ZMM register, writing a k mask?
写入 ZMM 寄存器会使 Skylake-X(或类似设备)CPU 无限期地处于降低最大涡轮增压的状态。 (SIMD instructions lowering CPU frequency and Dynamically determining where a rogue AVX-512 instruction is executing)想必Ice Lake也差不多
(解决方法:根据我在 中引用的@BeeOnRope 的评论,zmm16..31 不是问题
所以这个 strlen 可以只使用 vpxord xmm16,xmm16,xmm16
和 vpcmpeqb
与 zmm16。)
如果您有硬件,如何进行测试:
@BeeOnRope 发布 test code in an RWT thread:将 vbroadcastsd zmm15, [zero_dp]
替换为 vpcmpeqb k0, zmm0, [rdi]
作为 "dirtying" 指令,然后查看 运行 之后的循环是慢还是快.
我假设执行任何 512 位 uop 将暂时触发减少的 turbo(同时关闭矢量 ALU uops 的端口 1,而 512 位 uop 实际上在后端),但问题是:Will如果在 读取 ZMM 寄存器后从未使用 vzeroupper
,CPU 会自行恢复?
(And/or 以后的 SSE 或 AVX 指令会有转换惩罚或错误依赖吗?)
具体来说,像这样使用 insns 的 strlen
在返回之前需要 vzeroupper
吗?(实际上在任何真实的 CPU 上, and/or 如英特尔记录的面向未来的最佳实践。)假设以后的指令可能包括非 VEX SSE and/or VEX 编码 AVX1/2,而不仅仅是 GP 整数,以防相关到脏上 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?
(受 中的 strlen 的启发,如果对其向量 reg 归零进行了适当优化以使用更短的 VEX 而不是 EVEX 指令,它会看起来像这样。)
关键指令是 vpcmpeqb k0, zmm0, [rdi]
,它在 SKX 或 CNL 上解码为 2 个独立的微指令 (not micro-fused: retire-slots = 2.0):一个 512 位加载(到一个 512 位物理寄存器?)和一个ALU 比较成屏蔽寄存器。
但是没有架构 ZMM 寄存器是明确写入的,只能读取。所以大概至少 xsave
/xrstor
会清除任何 "dirty upper" 条件,如果在此之后存在的话。 (这不会在 Linux 上发生,除非在该内核上有一个实际的上下文切换到不同的用户-space 进程,或者线程迁移;仅仅进入内核中断不会导致它。所以这在主流 OS 下实际上仍然可以测试,如果你有硬件;我没有。)
我可以想象 SKX/CNL、and/or 冰湖的可能性:
- 无长期影响:最大涡轮恢复速度与
vzeroupper
一样快
- 最大 turbo 限制为 512 位速度,直到上下文切换。 (
xrstor
或等效清除任何脏上层状态标志,因为架构规则是干净的)。
- 最大 turbo 限制为 512 位速度,即使在上下文切换时也是如此,就像您 运行
vaddps zmm0,zmm0,zmm0
一样。 (肮脏的上层标志设置在保存和恢复的架构状态中。)这似乎是合理的,因为如果已知它们是干净的,xsaveopt
确实会跳过保存向量 regs 的上层 128 或 256。
我假设 kmovq
不会降低最大涡轮增压或触发任何其他 512 位 uop 效果。屏蔽寄存器的高 32 位通常只与 AVX512BW 一起用于 64 字节向量,但大概它们不会单独对屏蔽寄存器的前 32 位进行电源门控,只有前 32 字节 的矢量 regs。有一些用例,例如使用 kshift
或 kunpack
来处理 64 位掩码块(对于 load/store 或转移到整数 regs),即使您只生成或使用它们 32使用带有 YMM 或 XMM 寄存器的 AVX512VL 一次位。
PS:Xeon Phi 不受这些影响;当 运行 其他代码时,它不是为了超越重型 AVX512 而构建的,因为它是为 运行 AVX512 制作的。事实上 vzeroupper
非常慢,不推荐在 KNL / KNM 上使用。
我的示例使用 AVX512BW 的事实与问题无关,但所有主流(非 Xeon Phi)CPU 具有 AVX512 的都具有 AVX512BW。它只是一个很好的真实用例,使用 AVX512BW 排除 KNL 的事实是无关紧要的。
No,如果使用 zmm
寄存器作为 compa[=48] 之一,将 vpcmpeqb
写入掩码寄存器不会触发慢速模式=]ds,至少在 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
全速 运行 的唯一测试是 Scalar integer adds
,它根本没有 SSE/AVX 寄存器使用,而 scalar ucomis (w/ vzeroupper)
有明确的 vzeroupper
在每次测试之前,所以不会用脏鞋面执行。
然后,我把脏指令改成了你感兴趣的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 位向量和像 FMA 这样的重 FP 指令,因为预期。
写入 ZMM 寄存器会使 Skylake-X(或类似设备)CPU 无限期地处于降低最大涡轮增压的状态。 (SIMD instructions lowering CPU frequency and Dynamically determining where a rogue AVX-512 instruction is executing)想必Ice Lake也差不多
(解决方法:根据我在 vpxord xmm16,xmm16,xmm16
和 vpcmpeqb
与 zmm16。)
如果您有硬件,如何进行测试:
@BeeOnRope 发布 test code in an RWT thread:将 vbroadcastsd zmm15, [zero_dp]
替换为 vpcmpeqb k0, zmm0, [rdi]
作为 "dirtying" 指令,然后查看 运行 之后的循环是慢还是快.
我假设执行任何 512 位 uop 将暂时触发减少的 turbo(同时关闭矢量 ALU uops 的端口 1,而 512 位 uop 实际上在后端),但问题是:Will如果在 读取 ZMM 寄存器后从未使用 vzeroupper
,CPU 会自行恢复?
(And/or 以后的 SSE 或 AVX 指令会有转换惩罚或错误依赖吗?)
具体来说,像这样使用 insns 的 strlen
在返回之前需要 vzeroupper
吗?(实际上在任何真实的 CPU 上, and/or 如英特尔记录的面向未来的最佳实践。)假设以后的指令可能包括非 VEX SSE and/or VEX 编码 AVX1/2,而不仅仅是 GP 整数,以防相关到脏上 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?
(受
关键指令是 vpcmpeqb k0, zmm0, [rdi]
,它在 SKX 或 CNL 上解码为 2 个独立的微指令 (not micro-fused: retire-slots = 2.0):一个 512 位加载(到一个 512 位物理寄存器?)和一个ALU 比较成屏蔽寄存器。
但是没有架构 ZMM 寄存器是明确写入的,只能读取。所以大概至少 xsave
/xrstor
会清除任何 "dirty upper" 条件,如果在此之后存在的话。 (这不会在 Linux 上发生,除非在该内核上有一个实际的上下文切换到不同的用户-space 进程,或者线程迁移;仅仅进入内核中断不会导致它。所以这在主流 OS 下实际上仍然可以测试,如果你有硬件;我没有。)
我可以想象 SKX/CNL、and/or 冰湖的可能性:
- 无长期影响:最大涡轮恢复速度与
vzeroupper
一样快
- 最大 turbo 限制为 512 位速度,直到上下文切换。 (
xrstor
或等效清除任何脏上层状态标志,因为架构规则是干净的)。 - 最大 turbo 限制为 512 位速度,即使在上下文切换时也是如此,就像您 运行
vaddps zmm0,zmm0,zmm0
一样。 (肮脏的上层标志设置在保存和恢复的架构状态中。)这似乎是合理的,因为如果已知它们是干净的,xsaveopt
确实会跳过保存向量 regs 的上层 128 或 256。
我假设 kmovq
不会降低最大涡轮增压或触发任何其他 512 位 uop 效果。屏蔽寄存器的高 32 位通常只与 AVX512BW 一起用于 64 字节向量,但大概它们不会单独对屏蔽寄存器的前 32 位进行电源门控,只有前 32 字节 的矢量 regs。有一些用例,例如使用 kshift
或 kunpack
来处理 64 位掩码块(对于 load/store 或转移到整数 regs),即使您只生成或使用它们 32使用带有 YMM 或 XMM 寄存器的 AVX512VL 一次位。
PS:Xeon Phi 不受这些影响;当 运行 其他代码时,它不是为了超越重型 AVX512 而构建的,因为它是为 运行 AVX512 制作的。事实上 vzeroupper
非常慢,不推荐在 KNL / KNM 上使用。
我的示例使用 AVX512BW 的事实与问题无关,但所有主流(非 Xeon Phi)CPU 具有 AVX512 的都具有 AVX512BW。它只是一个很好的真实用例,使用 AVX512BW 排除 KNL 的事实是无关紧要的。
No,如果使用 zmm
寄存器作为 compa[=48] 之一,将 vpcmpeqb
写入掩码寄存器不会触发慢速模式=]ds,至少在 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
全速 运行 的唯一测试是 Scalar integer adds
,它根本没有 SSE/AVX 寄存器使用,而 scalar ucomis (w/ vzeroupper)
有明确的 vzeroupper
在每次测试之前,所以不会用脏鞋面执行。
然后,我把脏指令改成了你感兴趣的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 位向量和像 FMA 这样的重 FP 指令,因为预期。