从 avx/sse 掩码到 avx512 掩码的 "correct" 方法是什么?

What is the "correct" way to go from avx/sse masks to avx512 masks?

我有一些现有的 avx/sse 掩码是我用旧方法得到的:

auto mask_sse = _mm_cmplt_ps(a, b);
auto mask_avx = _mm_cmp_ps(a, b, 17);

在某些情况下,当将旧的 avx 代码与新的 avx512 代码混合时,我想将这些旧样式的掩码转换为新的 avx512 __mmask4__mmask8 类型。

我试过这个:

auto mask_avx512 = _mm_cmp_ps_mask(sse_mask, _mm_setzero_ps(), 25/*nge unordered quiet*/);

它似乎适用于比较的普通旧输出,但我不认为它会正确捕获可能与 sse4.1 一起使用的正 NAN _mm_blendv_ps

也有很好的旧 _mm_movemask_ps,但看起来它把掩码一直放在通用寄存器中,我需要用 _cvtu32_mask8 链接它才能拉出它回到专用掩码寄存器之一。

是否有更简洁的方法直接将旧式掩码中的符号位拉出到 k 个寄存器之一?

示例代码:

这是一个示例程序,它按照我上面提到的第一种方式进行掩码转换

#include "x86intrin.h"
#include <cassert>
#include <cstdio>

int main()
{
    auto a = _mm_set_ps(-1, 0, 1, 2);
    auto c = _mm_set_ps(3, 4, 5, 6);

    auto sse_mask    = _mm_cmplt_ps(a, _mm_setzero_ps());
    auto avx512_mask = _mm_cmp_ps_mask(sse_mask, _mm_setzero_ps(), 25);

    alignas(16) float v1[4];
    alignas(16) float v2[4];
    _mm_store_ps(v1, _mm_blendv_ps(a, c, sse_mask));
    _mm_store_ps(v2, _mm_mask_blend_ps(avx512_mask, a, c));

    assert(v1[0] == v2[0]);
    assert(v1[1] == v2[1]);
    assert(v1[2] == v2[2]);
    assert(v1[3] == v2[3]);
    return 0;
}

首先使用 AVX-512 比较内在函数获得 AVX-512 掩码(如 _mm_cmp_ps_mask);这将比先比较一个向量然后再转换它要高效得多,除非编译器为您优化掉这种低效率。 (考虑使用像 Agner Fog's VCL 这样的包装库来尝试抽象出差异。VCL 许可证最近从 GPL 更改为 Apache。)


但如果您确实需要它(例如,作为完成优化前的权宜之计),您不需要 FP 比较. _mm_cmp_ps 在 C 中产生 __m128 结果,但它不是 真正的 浮点向量 1。它是全一位/全零位。您只需要这些位,因此您正在寻找 vmovmskps 的 AVX-512 等价物,但进入 k 寄存器而不是 GP 整数。即 VPMOVD2M k, x/y/zmm 用于 32 位源元素。

   __m128 cmpvec = _mm_cmplt_ps(v, _mm_setzero_ps() );
   __mmask8 cmpmask = _mm_movepi32_mask( _mm_castps_si128(cmpvec) );   // <----

// equivalent to comparing into a mask in the first place:
   __mmask8 cmpmask = _mm_cmplt_ps_mask(v, _mm_setzero_ps(), _CMP_LT_OQ);

// equivalent to (if I got this right)
   __mmask8 cmpmask = _mm_fpclass_ps_mask(v, 0x40 | 0x10);  // negative | negative_inf

https://uops.info/ 现在关闭,否则我会检查 VPMOVD2M 与 VCMPPS 的延迟和执行端口到掩码(对于 UNORD 谓词)与 VFPCLASSPS。


脚注 1:您 可以 将 AVX-512 vfpclassps 用于掩码,甚至可以使用 vcmpps 谓词(如 UNORD)与自身进行比较以检测NAN 与否。但是我觉得那些比较慢。


I would need to chain it with a _cvtu32_mask8 to pull it back into one of the dedicated mask registers.

编译器当前做事的方式,__mmask8 只是 unsigned char 的类型定义,而 __mmask16unsigned short。它们可以在没有内在函数的情况下自由转换,无论好坏。但是在 asm 中,需要一条 kmovb k1, eax 指令才能将数据从 GP reg 获取到 k 掩码 reg,并且该指令只能 运行 在当前 CPU 的端口 5 上。