intel intrinsics 文档中的 "MAX" 指的是什么?

What is "MAX" referring to in the intel intrinsics documentation?

intel intrinsics guide 中,一些操作是使用术语“MAX”定义的。一个例子是__m256 _mm256_mask_permutexvar_ps (__m256 src, __mmask8 k, __m256i idx, __m256 a),定义为

FOR j := 0 to 7
    i := j*32
    id := idx[i+2:i]*32
    IF k[j]
        dst[i+31:i] := a[id+31:id]
    ELSE
        dst[i+31:i] := 0
    FI
ENDFOR
dst[MAX:256] := 0

。请注意此定义中的最后一行:dst[MAX:256] := 0MAX 指的是什么,这一行是否添加了任何有价值的信息?如果我必须做出假设,那么 MAX 可能意味着向量中的位数,在 _mm256 的情况下是 256。然而,这似乎并没有改变操作定义的任何内容,也可能被省略了。但是为什么会在那里呢?

dst[MAX:256] := 0 将第 256 位以上(包括)的所有位设置为零。它只与超过 256 位的寄存器有关。因此,如果寄存器的长度为 256 位,则 MAX 可以为 256;如果处理器使用 512 位寄存器,则 MAX 可以为 512。

寄存器中的位被编号为“左”的高索引和“右”的低索引。这符合我们写和谈论二进制数字的方式:100102 是 18 的二进制数字,位数为 4,表示 24 = 16 ,左边和位数0,代表20 = 1,右边.

R[m:n]表示寄存器R从mnm 是集合的“左”端,n 是集合的“左”端“右”端。如果m小于n,则为空集。因此,对于 512 位的寄存器,dst[511:256] := 0 表示将位 511 到 256 设置为零,而对于 256 位的寄存器,dst[255:256] := 0 表示什么也不做。

这个 pseudo-code 只对 assembly 文档有意义,它是从那里复制的,而不是对内在函数。 (英特尔第 2 卷 PDF 的 HTML scrape 记录了相应的 vpermps asm 指令。)

   ...
ENDFOR
DEST[MAXVL-1:VL] ← 0

(同一 asm 文档条目涵盖 VL = 128、256 和 512 位版本,即指令的向量宽度。)

在 asm 中,YMM 寄存器是 ZMM 寄存器的低半部分,写入 YMM 会将高位清零到 CPU 的最大支持向量宽度(就像将 EAX zero-extends 写入 RAX)。

您选择的内在函数是用于屏蔽版本的,因此它需要 AVX-512(EVEX 编码),因此 VLMAX 至少为 5121。如果掩码是一个常数all-ones,它可以针对 AVX2 VEX 编码进行优化,但两者仍然将整个寄存器的高位清零到 VLMAX。

这对内在函数没有意义

内在函数 API 只有 __m256__m512 类型; __m256 而不是 隐含地是 __m512 的低半部分。您可以使用 _mm512_castps256_ps512 得到一个 __m512 并且您的 __m256 作为低半部分,但是 the API documentation says结果的高 256 位未定义”。因此,如果您在函数 arg 上使用它,它不会强制它 vmovaps ymm7, ymm0 或某些东西 zero-extend 进入 ZMM 寄存器,以防调用者留下高垃圾。

如果您在来自此函数的内在函数的 __m256 上使用 _mm512_castps256_ps512,无论它是保留在 reg 中还是得到stored/reloaded,但这 不是 由 API 保证的。 (如果编译器选择将先前的计算与其他计算相结合,使用 512 位运算,您可能最终得到 non-zero 高半。)如果您想要高零,则没有等同于 _mm256_set_m128 (__m128 hi, __m128 lo),所以你需要一些其他明确的方式。


脚注 1:或者通过一些假设的未来扩展,VLMAX 又名 MAXVL 可能会更宽。它由 XCR0 的当前值决定。该文档告诉您这些指令仍然会归零为任何内容。

(我没有研究是否可以在支持 AVX-512 的机器上更改 VLMAX,或者它是否是 read-only。IDK 如果可以更改它,CPU 将如何处理它,就像可能根本 运行ning 512 位指令一样。主流操作系统当然不会这样做,即使它可能具有特权操作。)

SSE 没有任何定义的机制来扩展到更广泛的向量,一些现有代码(特别是 Windows 内核驱动程序)手动 saved/restored 一些 XMM 寄存器供自己使用。为了支持这一点,AVX 决定遗留 SSE 将保留 YMM/ZMM 寄存器的高位部分不变。但是为了 运行 有效地使用 non-VEX 遗留 SSE 编码的现有机器代码,它需要昂贵的状态转换(Haswell 和 Ice Lake)and/or 错误依赖(Skylake):

Intel 不会再犯这个错误,所以他们将 AVX 定义为归零到 CPU 支持的任何矢量宽度,并在每个 AVX 和 AVX-512 指令编码中清楚地记录它。因此 VEX 和 EVEX 可以自由混合,甚至有助于节省 machine-code size:

  • (none),答案讨论了为什么 SSE/AVX 惩罚是一回事的更多细节。
  • https://software.intel.com/en-us/forums/intel-isa-extensions/topic/301853 Agner Fog 的 2008 post 在英特尔关于 AVX 的论坛上,当它首次宣布时,指出了由于 SSE 缺乏远见而造成的问题。
  • - 有趣的是没有;由于无法通过旧版 SSE 指令访问它们,因此它们不能成为 dirty-uppers 问题的一部分。