如何解决首次使用 AVX 256 位向量会减慢 128 位向量和 AVX 标量操作
最初我试图重现 Agner Fog 的微体系结构指南部分“YMM 和 ZMM 向量指令的预热期”中描述的效果:
处理器在不使用时关闭向量执行单元的上部,以节省功耗。在大约 56,000 个时钟周期或 14 μs 的初始预热期间,具有 256 位向量的指令的吞吐量大约比正常情况慢 4.5 倍。
我得到了减速,虽然它看起来更接近 ~2x 而不是 4.5x。但是我发现在我的 cpu (Intel i7-9750H Coffee Lake) 上,速度减慢不仅影响 256 位操作,还影响 128 位向量操作和标量浮点操作(甚至 N 个 GPR-only XMM 触摸指令之后的指令)。
基准程序代码:
# Compile and run:
# clang++ ymm-throttle.S && ./a.out
.intel_Syntax noprefix
.data
L_F0:
.asciz "ref cycles = %u\n"
.p2align 5
L_C0:
.long 1
.long 2
.long 3
.long 4
.long 1
.long 2
.long 3
.long 4
.text
.set initial_scalar_warmup,5*1000*1000
.set iteration_count,30*1000
.set wait_count,50*1000
.global _main
_main:
# ---------- Initial warm-up
# It seems that we enter _main (at least in MacOS 11.2.2) in a "ymm warmed-up" state.
#
# Initial warm-up loop below is long enough for the processor to switch back to
# "ymm cold" state. It also may reduce dynamic-frequency scaling related measurements
# deviations (hopefully cpu is in full boost by the time we finish initial warmup loop).
vzeroupper
push rbp
mov ecx,initial_scalar_warmup
.p2align 4
_initial_loop:
add eax,1
add edi,1
add edx,1
dec ecx
jnz _initial_loop
# --------- Measure XMM
# TOUCH YMM.
# Test to see effect of touching unrelated YMM register
# on XMM performance.
# If "vpxor ymm9" below is commented out,then the xmm_loop below
# runs a lot faster (~2x faster).
vpxor ymm9,ymm9,ymm9
mov ecx,iteration_count
rdtsc
mov esi,eax
vpxor xmm0,xmm0,xmm0
vpxor xmm1,xmm1,xmm1
vpxor xmm2,xmm2,xmm2
vmovdqa xmm3,[rip + L_C0]
.p2align 5
_xmm_loop:
# Here we only do XMM (128-bit) VEX-encoded op. But it is triggering execution throttling.
vpaddd xmm0,xmm3,xmm3
add edi,1
add eax,1
dec ecx
jnz _xmm_loop
lfence
rdtsc
sub eax,esi
mov esi,eax # ESI = ref cycles count
# ------------- Print results
lea rdi,[rip + L_F0]
xor eax,eax
call _printf
vzeroupper
xor eax,eax
pop rbp
ret
问题:我的基准测试是否正确?对正在发生的事情的描述(如下)是否合理?
cpu 处于 AVX-cold 状态(在 ~675 µs 内没有执行 256 位/512 位指令)遇到带有 YMM (ZMM) 目标寄存器的单条指令。 cpu 立即切换到某种“过渡到 AVX-warm”状态。这大概需要 Agner 指南中提到的 ~100-200 个周期。而这个“过渡”期持续约 56,000 个周期。
在过渡期间 GPR 代码可以正常执行,但任何具有向量目标寄存器的指令(包括 128 位 XMM 或标量浮点指令,甚至包括 vmovq xmm0,rax
)都会对整个执行流水线应用节流。这会影响紧跟在 N 个周期的此类指令之后的仅 GPR 代码(不确定有多少;可能是 ~ 十几个周期的指令)。
也许节流限制了分派到执行单元的 µop 的数量(不管这些 µop 是什么;只要至少有一个 µop 具有向量目标寄存器)?
对我来说新的是,我认为在过渡期间节流将仅适用于 256 位(和 512 位)指令,但似乎任何具有向量寄存器目标的指令都会受到影响(以及大约 20-60 的 GPR-仅按照说明立即执行;无法在我的系统上进行更精确的测量)。
相关:an article at Travis Downs blog 的“仅电压转换”部分可能描述了相同的效果。虽然作者在过渡期间测量了YMM向量的性能,但结论是,在过渡期间遇到向量寄存器接触指令时,并不是向量的上半部分被拆分,而是对整个流水线进行了节流。 (编辑:博文在过渡期间没有测量 XMM 寄存器,这正是本文所测量的)。
解决方法
即使对于窄 SIMD 指令,您也看到节流这一事实是我称之为隐式加宽的行为的副作用。
基本上,在现代英特尔上,如果任何寄存器的高 128-255 位在 ymm0
到 ymm15
范围内,任何 > SIMD 指令在内部扩展为 256 位,因为高位需要清零,这需要寄存器文件中的完整 256 位寄存器供电,可能还需要 256 位 ALU 路径。因此,该指令对于 AVX 频率的作用就好像它是 256 位宽一样。
类似地,如果 任何 zmm 寄存器在 zmm0
到 zmm15
范围内的第 256 到 511 位是脏的,操作都会隐式地扩展到 512 位。
对于轻指令和重指令,加宽指令的类型与全宽指令的类型相同。也就是说,即使只有 128 位的 FMA 发生,一个 128 位的 FMA 被加宽为 512 位也充当“重型 AVX-512”。
这适用于所有使用 xmm/ymm 寄存器的指令,甚至标量 FP 操作。
请注意,这不仅仅适用于这个节流期:这意味着如果你有脏的上限,窄的 SIMD 指令(或标量 FP)将导致转换到更保守的 DVFS 状态,就像全宽指令可以。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。