如何有效地将 zmm 寄存器的低 64 位保存到内存中?

Posted

技术标签:

【中文标题】如何有效地将 zmm 寄存器的低 64 位保存到内存中?【英文标题】:How do I efficiently save the lower 64 bits of a zmm register to memory? 【发布时间】:2020-11-18 01:59:50 【问题描述】:

我正在使用 AVX512 对函数进行矢量化。这有效,除了需要将结果保存到内存的存储,它只是低 64 位数字。

inline square(double x)  return x * x; 

  v = in[pos];
  v0 = v - (a1 * v1 + a2 * v2 + a3 * v3 + a4 * v4);
  r = square(b0 * v0 + b1 * v1 + b2 * v2 + b3 * v3 + b4 * v4);
  out[idx] = r;
  v4 = v3;
  v3 = v2;
  v2 = v1;
  v1 = v0;

其中 a1 到 a4 和 b0 到 b4 是预先计算的因子。 v0 到 v4 首先设置为零,然后设置为输入值,如上所示。然后旋转该矩阵。

square() 计算后,我想将结果双精度保存在输出缓冲区中。该值在位 [63:0] 或寄存器“r”中。

我尝试了这个内在:

_mm512_store_epi64(out + idx, r);

但它需要超过 16 个字节的对齐方式(它的 SEGV 的地址是 16 的倍数),我需要一条使用 8 字节对齐方式的指令。生成的汇编指令是vmovdqa64,如下所示:

=> 0x0000555555555122 <+290>: vmovdqa64 %zmm0,(%rbx,%rax,8)

【问题讨论】:

这不是MOVQ 所做的吗?该页面将_mm_cvtsi128_si64 列为内在函数。 不过,我找不到合适的内在函数来在压缩双精度类型上使用它。 @NateEldredge 这不正确。 mm 指令是“遗留”的,因为它们使用 SSE,与 AVX 混合使用是一件很糟糕的事情(因为它们将顶部寄存器内容保存在 transitions 之间。) 【参考方案1】:

AVX512 为无掩码 vector 存储引入了愚蠢的误导性固有名称,例如 _mm512_store_epi64 用于 vmovdqa64 [mem], zmm,即 _mm512_store_si512。永远不要使用它们,但内在函数指南确实记录了它们的作用:https://software.intel.com/sites/landingpage/IntrinsicsGuide


转换为__m128i 并使用正常的低元素标量内在函数。你想要的asm指令是vmovq %xmm0, (%rbx,%rax,8)

__m512i 的低 128 位是__m128i,所以_mm512_castsi512_si128 是免费的。

uint64_t extractlow64(__m512i v) 
    return _mm_cvtsi128_si64(_mm512_castsi512_si128(v));

compiles to this with GCC

extractlow64(long long __vector(8)):
        vmovq   rax, xmm0
        ret                   # vzeroupper not needed: caller had to use ZMM to pass the arg

同样适用于__m512d

double extractlow_double(__m512d v) 
    return _mm_cvtsd_f64(_mm512_castpd512_pd128(v));

extractlow_double(double __vector(8)):
        ret
     # the low element of xmm0 (retval) is already the low element of zmm0

如果未优化存储,则在某处分配 double 结果将使编译器发出 vmovsd 存储。

【讨论】:

很有趣,因为文档 here _mm_cvtsd_f64() 说这是一个遗留的 SSE2 指令,these docs 说不要将 AVX 与 SSE 混合......但是,查看指令集参考书, VMOVQ 被明确标记为 AVX... 这很复杂。 @AlexisWilke:每条旧指令都有一个 AVX 编码,在助记符前面有一个 v。 AVX 编码与原始指令一起列出。此外,读取带有movq %xmm0, %rax 的向量寄存器可能没有问题;当 reg 的上半部分脏了时,只用旧的 SSE 指令编写 XMM0 将是一个潜在的问题。但无论如何,旧指令没有新的助记符,当您使用 -mavx(例如,作为 -march=skylake-avx512 的一部分)时,编译器总是会发出 VEX / EVEX 编码。

以上是关于如何有效地将 zmm 寄存器的低 64 位保存到内存中?的主要内容,如果未能解决你的问题,请参考以下文章

如何将两个32位整数组合成一个64位整数?

如何将 AVX512 寄存器 zmm26 中的 QuadWord 写入 rax 寄存器?

在单臂霓虹灯寄存器中有效地将 8 位数字扩展到 12 位

ARM NEON 内部函数将 D(64 位)寄存器转换为 Q(128 位)寄存器的低半部分,而上半部分未定义

有效地将 CPU 寄存器中的所有位设置为 1

Linux x86和x64的区别