ste*_*pan 4 assembly sse x86-64 simd avx
最初,我试图重现 Agner Fog 的微架构指南部分“YMM 和 ZMM 向量指令的预热期”中描述的效果,其中写道:
\n\n\n处理器在不使用时关闭向量执行单元的上部部分,以节省电力。在大约 56,000 个时钟周期或 14 \xce\xbcs 的初始预热期间,具有 256 位向量的指令的吞吐量大约比正常情况慢 4.5 倍。
\n
我得到了减速,尽管看起来更接近 2 倍而不是 4.5 倍。但我发现在我的 CPU(Intel i7-9750H Coffee Lake)上,速度下降不仅影响 256 位操作,还影响 128 位向量操作和标量浮点操作(甚至 N 个 GPR-仅限 XMM 触摸指令之后的指令)。
\n基准程序代码:
\n# Compile and run:\n# clang++ ymm-throttle.S && ./a.out\n\n.intel_syntax noprefix\n\n.data\nL_F0:\n .asciz "ref cycles = %u\\n"\n\n.p2align 5\nL_C0:\n .long 1\n .long 2\n .long 3\n .long 4\n .long 1\n .long 2\n .long 3\n .long 4\n\n.text\n\n.set initial_scalar_warmup, 5*1000*1000\n.set iteration_count, 30*1000\n.set wait_count, 50*1000\n\n.global _main\n_main:\n # ---------- Initial warm-up\n # It seems that we enter _main (at least in MacOS 11.2.2) in a "ymm warmed-up" state.\n #\n # Initial warm-up loop below is long enough for the processor to switch back to\n # "ymm cold" state. It also may reduce dynamic-frequency scaling related measurements\n # deviations (hopefully CPU is in full boost by the time we finish initial warmup loop).\n\n vzeroupper\n\n push rbp\n mov ecx, initial_scalar_warmup\n\n.p2align 4\n_initial_loop:\n add eax, 1\n add edi, 1\n add edx, 1\n\n dec ecx\n jnz _initial_loop\n\n # --------- Measure XMM\n\n # TOUCH YMM.\n # Test to see effect of touching unrelated YMM register\n # on XMM performance.\n # If "vpxor ymm9" below is commented out, then the xmm_loop below\n # runs a lot faster (~2x faster).\n vpxor ymm9, ymm9, ymm9\n\n mov ecx, iteration_count\n rdtsc\n mov esi, eax\n\n vpxor xmm0, xmm0, xmm0\n vpxor xmm1, xmm1, xmm1\n vpxor xmm2, xmm2, xmm2\n vmovdqa xmm3, [rip + L_C0]\n\n.p2align 5\n_xmm_loop:\n # Here we only do XMM (128-bit) VEX-encoded op. But it is triggering execution throttling.\n vpaddd xmm0, xmm3, xmm3\n add edi, 1\n add eax, 1\n\n dec ecx\n jnz _xmm_loop\n\n lfence\n rdtsc\n sub eax, esi\n mov esi, eax # ESI = ref cycles count\n\n # ------------- Print results\n\n lea rdi, [rip + L_F0]\n xor eax, eax\n call _printf\n\n vzeroupper\n xor eax, eax\n pop rbp\n ret\xe2\x80\x8b\nRun Code Online (Sandbox Code Playgroud)\n问题:我的基准正确吗?对正在发生的事情的描述(如下)看起来合理吗?
\nCPU 处于 AVX 冷状态(约 675 \xc2\xb5s 内未执行 256 位/512 位指令)遇到带有 YMM (ZMM) 目标寄存器的单个指令。CPU 立即切换到某种“过渡到 AVX-warm”状态。这大概需要 Agner 指南中提到的大约 100-200 个周期。这个“过渡”期持续约 56\'000 个周期。
\n在过渡期间,GPR 代码可以正常执行,但任何具有向量目标寄存器的指令(包括 128 位 XMM 或标量浮点指令,甚至包括vmovq xmm0, rax)都会对整个执行管道进行限制。这会影响紧随此类指令的 N 个周期的仅 GPR 代码(不确定有多少;可能相当于指令的十几个周期)。
也许限制会限制调度到执行单元的 \xc2\xb5ops 数量(无论这些 \xc2\xb5ops 是什么;只要至少有一个带有向量目标寄存器的 \xc2\xb5ops)?
\n对我来说,这里的新内容是,我认为在过渡期间限制将仅适用于 256 位(和 512 位)指令,但似乎任何具有向量寄存器目标的指令都会受到影响(以及~20-60 探地雷达 - 仅立即遵循说明;无法在我的系统上进行更精确的测量)。
\n相关:Travis Downs 博客上一篇文章的“仅电压转换”部分可能描述了相同的效果。尽管作者在过渡期间测量了 YMM 向量的性能,但得出的结论是,并不是向量的上半部分被分割,而是在过渡期间遇到向量寄存器接触指令时对整个管道进行了限制。(编辑:博客文章没有在过渡期间测量 XMM 寄存器,而这正是本文所测量的)。
\n事实上,即使对于窄 SIMD 指令,您也会看到限制,这是我称为“隐式加宽”的行为的副作用。
基本上,在现代 Intel 上,如果范围内的任何寄存器的高128-255位是脏的,任何SIMD 指令都会在内部扩展为 256 位,因为高位需要清零,这需要完整的 256 位寄存器在要供电的寄存器文件中,也可能在 256 位 ALU 路径中。因此,该指令对于 AVX 频率而言就像 256 位宽一样。ymm0ymm15
类似地,如果 到范围内的任何 zmm 寄存器上的位 256 到 511 是脏的,则操作将隐式扩展到 512 位。zmm0zmm15
出于轻指令与重指令的目的,加宽指令的类型与全宽指令的类型相同。也就是说,扩展至 512 位的 128 位 FMA 充当“重型 AVX-512”,即使仅出现 128 位 FMA。
这适用于使用 xmm/ymm 寄存器的所有指令,甚至是标量 FP 操作。
请注意,这不仅仅适用于此节流周期:这意味着如果上层脏了,窄 SIMD 指令(或标量 FP)将导致转换到更保守的 DVFS 状态,就像全角指令一样。