从 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
的类型定义,而 __mmask16
是 unsigned short
。它们可以在没有内在函数的情况下自由转换,无论好坏。但是在 asm 中,需要一条 kmovb k1, eax
指令才能将数据从 GP reg 获取到 k 掩码 reg,并且该指令只能 运行 在当前 CPU 的端口 5 上。
我有一些现有的 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
的类型定义,而 __mmask16
是 unsigned short
。它们可以在没有内在函数的情况下自由转换,无论好坏。但是在 asm 中,需要一条 kmovb k1, eax
指令才能将数据从 GP reg 获取到 k 掩码 reg,并且该指令只能 运行 在当前 CPU 的端口 5 上。