首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用AVX-512实现16位和32位整数插入和提取操作?

如何使用AVX-512实现16位和32位整数插入和提取操作?
EN

Stack Overflow用户
提问于 2019-10-09 12:16:56
回答 2查看 1.2K关注 0票数 3

AVX有在__m256i向量中插入和提取16位和32位整数的指令:_mm256_insert_epi16_mm256_insert_epi32_mm256_extract_epi16_mm256_extract_epi32

然而,AVX-512似乎没有同等的指令.对于__m512i向量,实现这些方法的适当方法是什么?即

  • __m512i _mm512_insert_epi16(__m512i a, __int16 i, int index)
  • __m512i _mm512_insert_epi32(__m512i a, __int32 i, int index)
  • int _mm512_extract_epi16(__m512i a, int index)
  • int _mm512_extract_epi32(__m512i a, int index)
EN

回答 2

Stack Overflow用户

发布于 2019-10-09 13:12:25

相关信息:

  • 如何将QuadWord从AVX512寄存器zmm26写入rax寄存器? -大部分都适用于提取32位和16位的元素。
  • m256i向量显示了插入,其中大部分应该适用于32位或16位的元素。(虽然vpblendw重复了两条车道的混合控制,但与vpblendd不同)。而这并不能利用AVX512的优势,比如合并蒙面广播。
  • 从GP regs加载xmm展示了AVX512如何使用合并屏蔽广播.但我没有费心为asm写一些内在的东西。

AVX有instructions,用于在__m256i向量中插入和提取16位和32位整数:

不,它没有,_mm256_insert_epi16 epi32 本质是“假的”;它们必须被多个指令模仿,就像_mm_set_epi32(a,b,c,d)不是任何单一指令的内在一样。

IDK为什么英特尔选择为AVX2 1/2而不是AVX512版本提供它们;也许他们后来意识到他们不应该为AVX2提供这些版本,以避免欺骗那些假设那些本质只需要一次洗牌的人编写效率低下的代码。但是他们不能在不破坏现有代码的情况下删除现有的代码。

不幸的是,vpinsrd ymm_dst, ymm_src, r/m32, imm8 (或ZMM)不存在,只有xmm。(https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq)。XMM版本在__m256i上是不可用的,因为它对上面的128位进行零点。请参阅使用ymm寄存器作为“类似内存”的存储位置。 (您可以使用pinsrd xmm, r/m32, imm的遗留SSE编码将其插入到YMM的低128位中,但由于SSE/AVX过渡刑罚在那里的工作方式,在哈斯韦尔和冰湖上的速度太慢了。但对Skylake或Ryzen没问题。不过,编译器永远不会发出这样的信息。)

_mm256_insert_epi32可以使用AVX2编译到广播加载,vpblendd可以从内存中插入一个dword。或者更糟的是,使用寄存器中的整数,编译器可能会将其vmovd到xmm reg,将其广播到YMM,然后进行混合。(就像我在m256i向量中展示的手工操作一样)

“适当的”实现取决于周围的代码.

如果要插入的元素超过一个,则可能需要在插入之前将它们混合在一起。甚至考虑向量存储,多标量存储,然后向量重新加载,尽管存储转发失速。或者,如果延迟临界路径通过向量,而不是标量,则标量存储/向量重新加载来提供混合数据。如果你有很多小的标量元素,这可能是值得的。

然而,对于单个,AVX512F实际上有一些很好的功能:它有两个输入洗牌,比如vpermt2d,您可以使用它将一个元素从一个x/y/zmm的底部插入到另一个向量中的任何位置(将另一个向量中的所有其他目标元素作为源)。

,但这里最有用的是蒙面广播: uops.info确认VPBROADCASTW zmm0{k1}, eax是一个单一的uop指令,从向量到向量(用于合并)和从掩码到向量的三个周期延迟。<= 5周期延迟从eax到合并结果。唯一的问题是设置掩码,但希望它能被提升出一个不变量插入位置的循环。

代码语言:javascript
运行
复制
#include <immintrin.h>
#include <stdint.h>
__m512i _mm512_insert32(__m512i target, uint32_t x, const int pos)
{
    return _mm512_mask_set1_epi32(target, 1UL<<pos, x);
}

论哥德波特编译到此asm:

代码语言:javascript
运行
复制
# gcc8.3 -O3 -march=skylake-avx512
_mm512_insert32(long long __vector(8), unsigned int, int):
        mov     eax, 1
        shlx    eax, eax, esi
        kmovw   k1, eax                    # mask = 1<<pos
        vpbroadcastd    zmm0{k1}, edi
        ret

(gcc9浪费额外的指令,无缘无故地复制ESI )。

使用编译时间常数pos,您可以得到像mov eax,2 /kmovw k1, eax这样的代码;蒙面广播可能仍然是最好的选择。

这种方法适用于8、16、32或64位元素。8和16当然需要AVX512BW来进行vpbroadcastb/w窄广播,而32和64只需要AVX512F。

摘录:

只需将您想要的元素洗牌到__m512i的底部,在那里您可以使用_mm_cvtsi128_si32。(_mm512_castsi512_si128后)一个有用的洗牌是valignd通过dword元素来移位或旋转,这样可以有效地将任何元素放到向量的底部,而不需要矢量控制。https://www.felixcloutier.com/x86/valignd:valignq

票数 6
EN

Stack Overflow用户

发布于 2019-10-18 12:03:07

要完成彼得的回答,以下是16位和32位插入/提取方法的实现:

代码语言:javascript
运行
复制
#if defined(__GNUC__)

int _mm512_cvtsi512_si32(__m512i a)
{
    __v16si b = (__v16si) a;
    return b[0];
}

#endif

__m512i _mm512_insert_epi16(__m512i target, const std::int16_t x, const int index)
{
    return _mm512_mask_set1_epi16(target, 1UL << index, x);
}
static inline __m512i _mm512_insert_epi32(__m512i target, const std::int32_t x, const int index)
{
    return _mm512_mask_set1_epi32(target, 1UL << index, x);
}

template <int index>
int _mm512_extract_epi32(__m512i target)
{
    return _mm512_cvtsi512_si32(_mm512_alignr_epi32(target, target, index));
}
template <int index>
int  _mm512_extract_epi16(__m512i target)
{
    return (_mm512_extract_epi32<index / 2>(target) >> (index % 2 ? 16 : 0)) & 0xFFFF;
}

示例

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58303958

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档