AVX具有将16位和32位整数插入和提取到__m256i
,_mm256_insert_epi16
,_mm256_insert_epi32
,_mm256_extract_epi16
向量中的指令。
但是,AVX-512似乎没有等效的说明。对_mm256_extract_epi32
向量实现这些方法的合适方法是什么?即
__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)
答案 0 :(得分:4)
相关:
vpblendw
与vpblendd
不同,这两个通道都重复了混合控制)。而且这没有利用AVX512的优势,例如合并屏蔽的广播。AVX具有指令,用于将16位和32位整数插入和提取到__m256i向量中:
不,它不是,_mm256_insert_epi16
和epi32
内在函数是“伪造的”;它们必须由多条指令模拟,_mm_set_epi32(a,b,c,d)
并不是任何一条指令都固有的。
IDK为什么英特尔选择为AVX1 / 2而不是AVX512版本提供它们;为什么?也许他们认为,如果他们假设这些代码只花费一次洗牌,就会愚弄人们编写低效率的代码。
vpinsrd ymm_dst, ymm_src, r/m32, imm8
(或ZMM)不存在,只有xmm。 (https://www.felixcloutier.com/x86/pinsrb:pinsrd:pinsrq)。 XMM版本无法在__m256i
上使用,因为它会将高128位清零。请参阅Using ymm registers as a "memory-like" storage location(您可以使用pinsrd xmm, r/m32, imm
的旧版SSE编码插入YMM的低128位,但是在Haswell上这是灾难性的缓慢,因为SSE / AVX过渡惩罚在那里起作用。但是很好在Skylake或Ryzen上运行。不过,编译器永远不会发出该信号。)
_mm256_insert_epi32
可能会与AVX2一起编译以广播负载,而vpblendd
可能会从内存中插入dword。或更糟糕的是,对于位于寄存器中的整数,编译器可能会vmovd
将其{xmm reg}广播到YMM,然后进行混合。 (就像我在Move an int64_t to the high quadwords of an AVX2 __m256i vector中展示的那样手工完成)
“适当的”实现取决于周围的代码。
如果要插入的元素超过1个,则可能需要在插入之前将它们一起洗牌。甚至考虑矢量存储,多个标量存储,然后重新加载矢量,尽管有存储转发停滞。或者,如果延迟关键路径通过矢量(而不是标量),则标量存储/矢量重新加载以提供混合。如果您有很多小标量元素,则可能值得。
但是,对于单个插入,AVX512F实际上具有一些不错的功能:它具有vpermt2d
之类的2输入随机播放,可用于从一个x /的底部插入元素y / zmm移到另一个向量的任何位置(将另一个向量中的所有其余目标元素作为源)。
但此处最有用的是屏蔽广播: uops.info confirms VPBROADCASTW zmm0{k1}, eax
是单uup指令,从向量到向量有3个周期的延迟(用于合并),从面具到矢量从eax到合并结果的周期延迟小于等于5。唯一的问题是设置遮罩,但是希望可以将其从循环中吊起,以实现不变的插入位置。
#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);
}
将on Godbolt编译为该asm:
# 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位元素。对于vpbroadcastb/w
窄广播,当然8和16需要AVX512BW,而32和64仅需要AVX512F。
只需将想要的元素随机排列到__m512i
的底部即可使用_mm_cvtsi128_si32
。 (在_mm512_castsi512_si128
之后)。 valignd
很有用,可以通过dword元素进行移位或旋转,使您无需向量控件即可有效地将任何元素移到向量的底部。 https://www.felixcloutier.com/x86/valignd:valignq
答案 1 :(得分:0)
要完成Peter's answer,以下是16和32位插入/提取方法的实现:
#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;
}
请参见example。