从 avx/sse 掩码到 avx512 掩码的“正确”方法是什么?

问题描述

我有一些现有的 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 _mm_blendv_ps 一起使用的正 NAN。

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

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

示例代码

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

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

int main()
{
    auto a = _mm_set_ps(-1,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,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 掩码(如 class Player_sprite_Health(pygame.sprite.Sprite): def __init__(self): self.image = pygame.image.load("images/HUD/hud_heartFull.png") self.rect = self.rect = self.image.get_rect(topleft = (35,10)) def healthDeleted(self): ''' Upon collision with an enemy object this function will be called It's purpose is to remove health ''' );除非编译器为您优化了这种低效率,否则这将比比较向量然后转换它的效率要高得多。 (考虑使用像 Agner Fog's VCL 这样的包装库来尝试抽象出差异。VCL 许可证最近从 GPL 更改为 Apache。)


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

k

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


脚注 1:您可以将 AVX-512 __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 用于掩码,或者甚至与 UNORD 之类的 vfpclassps 谓词进行比较,以检测或不检测 NAN。但我认为这些比较慢。


我需要用 vcmpps 将其链接起来,以将其拉回专用掩码寄存器之一。

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