我正在使用新的 AVX512 指令集,并尝试了解它们的工作原理以及如何使用它们。
我尝试的是交错由掩码选择的特定数据。我的小基准测试将 x*32 字节的对齐数据从内存加载到两个向量寄存器中,并使用动态掩码对它们进行压缩(图 1)。生成的向量寄存器分散到内存中,因此两个向量寄存器是交错的(图 2)。
图 1:使用相同的动态创建掩码压缩两个数据向量寄存器。
图 2:分散存储以交错压缩数据。
我的代码如下所示:
void zipThem( uint32_t const * const data, __mmask16 const maskCompress, __m512i const vindex, uint32_t * const result ) {
/* Initialize a vector register containing zeroes to get the store mask */
__m512i zeroVec = _mm512_setzero_epi32();
/* Load data */
__m512i dataVec_1 = _mm512_conflict_epi32( data );
__m512i dataVec_2 = _mm512_conflict_epi32( data + 16 );
/* Compress the data */
__m512i compVec_1 = _mm512_maskz_compress_epi32( maskCompress, dataVec_1 );
__m512i compVec_2 = _mm512_maskz_compress_epi32( maskCompress, dataVec_2 );
/* Get the store mask by compare the compressed register with the zero-register (4 means !=) */
__mmask16 maskStore = _mm512_cmp_epi32_mask( zeroVec, compVec_1, 4 );
/* Interleave the selected data */
_mm512_mask_i32scatter_epi32(
result,
maskStore,
vindex,
compVec_1,
1
);
_mm512_mask_i32scatter_epi32(
result + 1,
maskStore,
vindex,
compVec_2,
1
);
}
我用
-O3 -march=knl -lmemkind -mavx512f -mavx512pf
我将此方法称为 100'000'000 个元素。为了真正了解分散存储的行为,我用不同的 maskCompress 值重复了这个测量。我预计执行所需的时间和 maskCompress 中设置的位数之间存在某种依赖性。但我观察到,测试需要大致相同的时间来执行。以下是性能测试的结果:
图 3:测量结果。x 轴表示写入元素的数量,具体取决于 maskCompressed。y 轴显示性能。
可以看出,当更多的数据实际写入内存时,性能会变得更高。
我做了一些研究,得出了这个结论:avx512 的指令延迟。在给定的链接之后,所用指令的延迟是恒定的。但老实说,我对这种行为有点困惑。
关于 Christoph 和 Peter 的回答,我稍微改变了做法。因此,我不知道如何使用 unpackhi / unpacklo 来交错稀疏向量寄存器,我只是将 AVX512 压缩内在函数与 shuffle (vpermi) 结合起来:
int zip_store_vpermit_cnt(
uint32_t const * const data,
int const compressMask,
uint32_t * const result,
std::ofstream & log
) {
__m512i data1 = _mm512_undefined_epi32();
__m512i data2 = _mm512_undefined_epi32();
__m512i comp_vec1 = _mm512_undefined_epi32();
__m512i comp_vec2 = _mm512_undefined_epi32();
__mmask16 comp_mask = compressMask;
__mmask16 shuffle_mask;
uint32_t store_mask = 0;
__m512i shuffle_idx_lo = _mm512_set_epi32(
23, 7, 22, 6,
21, 5, 20, 4,
19, 3, 18, 2,
17, 1, 16, 0 );
__m512i shuffle_idx_hi = _mm512_set_epi32(
31, 15, 30, 14,
29, 13, 28, 12,
27, 11, 26, 10,
25, 9, 24, 8 );
std::size_t pos = 0;
int pcount = 0;
int fullVec = 0;
for( std::size_t i = 0; i < ELEM_COUNT; i += 32 ) {
/* Loading the current data */
data1 = _mm512_maskz_compress_epi32( comp_mask, _mm512_load_epi32( &(data[i]) ) );
data2 = _mm512_maskz_compress_epi32( comp_mask, _mm512_load_epi32( &(data[i+16]) ) );
shuffle_mask = _mm512_cmp_epi32_mask( zero, data2, 4 );
/* Interleaving the two vector register, depending on the compressMask */
pcount = 2*( __builtin_popcount( comp_mask ) );
store_mask = std::pow( 2, (pcount) ) - 1;
fullVec = pcount / 17;
comp_vec1 = _mm512_permutex2var_epi32( data1, shuffle_idx_lo, data2 );
_mm512_mask_storeu_epi32( &(result[pos]), store_mask, comp_vec1 );
pos += (fullVec) * 16 + ( ( 1 - ( fullVec ) ) * pcount ); // same as pos += ( pCount >= 16 ) ? 16 : pCount;
_mm512_mask_storeu_epi32( &(result[pos]), (store_mask >> 16) , comp_vec2 );
pos += ( fullVec ) * ( pcount - 16 ); // same as pos += ( pCount >= 16 ) ? pCount - 16 : 0;
//a simple _mm512_store_epi32 produces a segfault, because the memory isn't aligned anymore :(
}
return pos;
}
这样,两个向量寄存器中的稀疏数据可以交错。不幸的是,我必须手动计算商店的掩码。这似乎相当昂贵。可以使用 LUT 来避免计算,但我认为这不是应该的方式。
我知道这不是通常的方式,但我有 3 个与该主题相关的问题,我希望有人能帮助我。
为什么只有一个设置位的屏蔽存储需要与设置所有位的屏蔽存储相同的时间?
有没有人有一些经验或者是否有很好的文档来了解 AVX512 分散存储的行为?
有没有更简单或更高效的方法来交错两个向量寄存器?
谢谢你的帮助!
真挚地