X86 AVX512中索引数组存储的最快方式?
我对以下表格进行了操作:X86 AVX512中索引数组存储的最快方式?,x86,avx,avx512,X86,Avx,Avx512,我对以下表格进行了操作: for (I=0;I<31;I++) { dst[index1[I]]=src1[I]; dst[index2[I]]=src2[I]; } 用于(I=0;I在当前的CPU上,AVX-512分散指令不是超快的,在Skylake-X上每个时钟周期少于一个64位元素,在Ice Lake1上刚好超过一个qword/时钟。手动分散应该比在64位分散指令中模拟128位分散更好,但如果你是c,你可以在两种方式下进行基准测试愤怒的 特别是如果索引可以在index1和ind
for (I=0;I<31;I++)
{
dst[index1[I]]=src1[I];
dst[index2[I]]=src2[I];
}
用于(I=0;I在当前的CPU上,AVX-512分散指令不是超快的,在Skylake-X上每个时钟周期少于一个64位元素,在Ice Lake1上刚好超过一个qword/时钟。手动分散应该比在64位分散指令中模拟128位分散更好,但如果你是c,你可以在两种方式下进行基准测试愤怒的
特别是如果索引可以在index1和index2之间重叠(碰撞),则4个单独的128位存储几乎肯定比检查索引向量对之间的冲突要好。注意,如果idx1[1]==idx2[0],从src1散射4x元素,然后从src2散射4x元素,将得到不同的最终结果
。按照原始源代码顺序,该dst元素将获得src1[1]
,但如果您不小心,它将获得src2[0]
对于128位元素,可能只需执行512位加载,并使用vmovdqu xmm
(通过\u mm512\u castsi512\u si128
\u mm\u storeu\u si128
)和3x\u mm512\u extracti64x2\u epi64
(vextract64x2)存储进行手动分散
或者256位加载和vmovdqu xmm
+vextract128
存储。但是如果您在周围的代码中使用512位向量,您也可以在这里使用它们;您已经支付了CPU频率和执行端口关闭成本
如果您可以让编译器加载64位索引数据,并使用mov-eax,edx
/shr-rdx,32
,将32位的两部分分开,以保存内存加载/存储端口,这可能会很好。这可能与GNU Ctypedef uint64_64_u属性((may_-alias,aligned(4));
有关
脚注1:例如,Skylake-X上的vpscatterdq zmm
吞吐量为每11个循环一次,最佳情况。或冰湖上的吞吐量为每7个循环一次
这是每11个周期4倍128位存储。手动128位分散可能每2个周期至少1倍128位存储,甚至可能每时钟1次。或者在冰湖上更快,有2个存储端口和2个加载端口,以及更宽的前端
分散指令也是前端的大量UOP:SKX或ICL上分别为26或19
但只是为了好玩,模拟128位散射:
我们可以使用64位元素分散来模拟128位元素分散,例如\u mm512\u i32scatter\u epi64
()。或者\u mm512\u i64scatter\u epi64
(VPSCATTERQQ),如果您的索引需要为64位,则为加载索引节省内存带宽
生成一个索引向量,用于将连续的qword元素对存储到index1[I]*2
和index1[I]*2+1
分散中的比例因子只能是1、2、4或8,与x86索引寻址模式相同。如果您可以将“索引”存储为字节偏移量而不是元素偏移量,这可能有助于提高效率。否则,您必须先将每个索引输入向量加倍(将其添加到自身)
然后将其与递增的副本交错,可能与vpermt2d
void scatter(char *dst, __m512i data_lo, __m512i data_hi, __m256i idx)
{
idx = _mm256_add_epi32(idx,idx);
__m256i idx1 = _mm256_sub_epi32(idx, _mm256_set1_epi32(-1));
const __m256i interleave_lo = _mm256_set_epi32(11,3, 10,2, 9,1, 8,0);
const __m256i interleave_hi = _mm256_set_epi32(15,7, 14,6, 13,5, 12,4);
__m256i idx_lo = _mm256_permutex2var_epi32(idx, interleave_lo, idx1); // vpermt2d
__m256i idx_hi = _mm256_permutex2var_epi32(idx, interleave_hi, idx1);
_mm512_i32scatter_epi64(dst, idx_lo, data_lo, 8);
_mm512_i32scatter_epi64(dst, idx_hi, data_hi, 8);
}
显示它如何在循环中编译。(默认情况下,叮当声完全展开它。)等等,所有数组都有128b元素,甚至索引?仅限x86-64有64位指针,那又有什么意义呢?或者你是说只有dst和2个源?如果index1/2是无符号的uu int128
,你想在索引数组时截断它们的指针宽度吗?这是唯一有意义的事情。在我的回答中,我假设索引可能是32位以节省空间。我应该c更正此错误。索引数组元素为32位。