【问题标题】:First use of AVX 256-bit vectors slows down 128-bit vector and AVX scalar ops首次使用 AVX 256 位向量会减慢 128 位向量和 AVX 标量操作
【发布时间】:2021-06-26 15:50:29
【问题描述】:

最初我试图重现 Agner Fog 的微架构指南部分“YMM 和 ZMM 矢量指令的预热期”中描述的效果,其中说:

处理器在不使用时关闭向量执行单元的上部,以节省电力。在大约 56,000 个时钟周期或 14 μs 的初始预热期间,具有 256 位向量的指令的吞吐量比正常情况慢大约 4.5 倍。

我的速度变慢了,尽管它似乎更接近 2 倍而不是 4.5 倍。但我发现在我的 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)都会对整个执行管道应用节流。这会影响 GPR-only 代码紧跟在这样的指令之后的 N 周期(不确定有多少;可能是大约十个周期的指令)。

也许节流限制了分派到执行单元的微操作数(不管这些微操作是什么;只要至少有一个带有向量目标寄存器的微操作)?


对我来说,这里的新功能是我认为在过渡期间,节流仅适用于 256 位(和 512 位)指令,但似乎任何具有向量寄存器目标的指令都会受到影响(以及大约 20-60 只 GPR,仅在说明后立即执行;无法在我的系统上更精确地测量)。


相关:an article at Travis Downs blog 的“仅电压转换”部分可能描述了相同的效果。虽然作者在过渡期间测量了 YMM 向量的性能,但得出的结论是,在过渡期间遇到向量寄存器触摸指令时,并不是向量的上部被分割,而是对整个流水线进行了节流。 (编辑:这篇博文在过渡期间没有测量 XMM 寄存器,这正是这篇博文所测量的)。

【问题讨论】:

  • 这是Haswell AVX/FMA latencies tested 1 cycle slower than Intel's guide says吗?您的标题提到了“SSE”标量操作,但我仍然只看到像 vaddss 这样的 AVX 编码,而不是 addss。如果您确实有任何旧版 SSE,请参阅 Why is this SSE code 6 times slower without VZEROUPPER on Skylake?可能。
  • 不,站外链接不足以解决 Stack Overflow 问题。在问题本身中包含minimal reproducible example。 (最好有一个异地链接以获得更多细节和更多变化,但至少代码的基本部分,可能减去一些样板,应该在问题中。就像你在这里一样,但重要的部分没有注释变化。)
  • @PeterCordes 完成,我现在只包含了要点的主体而不是伪代码。让我知道是否足够清楚。
  • @Noah “所有指令都受到电压转换期间 IPC 降低的影响”我认为您可能在这里遗漏了一个重要细节。并非所有指令都受电压转换本身的影响。 GPR 代码将在过渡期间正常运行。但是如果在转换过程中看到向量接触指令并且 CPU 处于脏上层状态,那么它会开始节流 N 个周期。 节流会影响所有指令。我很惊讶即使是 128 位 AVX 指令也会导致限制,似乎没有提到。
  • 正确:如果指令窗口中有任何“宽”指令,则会发生限制,并且在发生限制时,它会影响所有指令(SIMD 或其他)。令人困惑的部分是“宽”指令在大多数情况下包括窄 SIMD(请参阅答案)。

标签: assembly x86-64 sse simd avx


【解决方案1】:

即使对于狭窄的 SIMD 指令,您也会看到节流这一事实是我称之为隐式扩展的行为的副作用。

基本上,在现代 Intel 上,如果 any 寄存器在 ymm0ymm15 范围内的高 128-255 位是脏的,则 any SIMD 指令是内部扩展为 256 位,因为需要将高位归零,这需要为寄存器文件中的完整 256 位寄存器供电,并且可能还需要 256 位 ALU 路径。因此,该指令用于 AVX 频率,就好像它是 256 位宽一样。

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

就轻量指令与重指令而言,加宽指令的类型与全宽指令的类型相同。也就是说,即使只有 128 位的 FMA 发生,128 位的 FMA 也被扩展为 512 位,充当“重型 AVX-512”。

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

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

【讨论】:

  • 这是一个很好的解释,谢谢!最后一个注释也很有启发性,我想即使是仅使用 AVX 的代码也使用 zeroupper 的一个很好的理由。
猜你喜欢
  • 2014-06-05
  • 2019-03-02
  • 2021-03-25
  • 2011-11-04
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
  • 2013-06-10
  • 2015-07-30
相关资源
最近更新 更多