微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

首次使用 AVX 256 位向量会减慢 128 位向量和 AVX 标量操作

如何解决首次使用 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 位在 ymm0ymm15 范围内,任何 > SIMD 指令在内部扩展为 256 位,因为高位需要清零,这需要寄存器文件中的完整 256 位寄存器供电,可能还需要 256 位 ALU 路径。因此,该指令对于 AVX 频率的作用就好像它是 256 位宽一样。

类似地,如果 任何 zmm 寄存器在 zmm0zmm15 范围内的第 256 到 511 位是脏的,操作都会隐式地扩展到 512 位。

对于轻指令和重指令,加宽指令的类型与全宽指令的类型相同。也就是说,即使只有 128 位的 FMA 发生,一个 128 位的 FMA 被加宽为 512 位也充当“重型 AVX-512”。

这适用于所有使用 xmm/ymm 寄存器的指令,甚至标量 FP 操作。

请注意,这不仅仅适用于这个节流期:这意味着如果你有脏的上限,窄的 SIMD 指令(或标量 FP)将导致转换到更保守的 DVFS 状态,就像全宽指令可以。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。