正如@MaratDukhan 所注意到的,_mm256_shuffle_epi8(即 ymm-s 的 VPSHUFB)不会执行完整的 32 字节随机播放。至于我,就有点可惜了……
这就是为什么要在没有 AVX2 的情况下模拟它,您可以简单地将每个寄存器分成两半,置换每一半,然后组合在一起:
//AVX only
__m256i _emu_mm256_shuffle_epi8(__m256i reg, __m256i shuf) {
__m128i reg0 = _mm256_castsi256_si128(reg);
__m128i reg1 = _mm256_extractf128_si256(reg, 1);
__m128i shuf0 = _mm256_castsi256_si128(shuf);
__m128i shuf1 = _mm256_extractf128_si256(shuf, 1);
__m128i res0 = _mm_shuffle_epi8(reg0, shuf0);
__m128i res1 = _mm_shuffle_epi8(reg1, shuf1);
__m256i res = _mm256_setr_m128i(res0, res1);
return res;
}
如果你真的想完全洗牌 32 字节的寄存器,你可以按照this paper 的方法。将每一半与每一半洗牌,然后将结果混合在一起。如果没有 AVX2,它会是这样的:
//AVX only
__m256i _emu_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) {
__m128i reg0 = _mm256_castsi256_si128(reg);
__m128i reg1 = _mm256_extractf128_si256(reg, 1);
__m128i shuf0 = _mm256_castsi256_si128(shuf);
__m128i shuf1 = _mm256_extractf128_si256(shuf, 1);
__m128i res00 = _mm_shuffle_epi8(reg0, shuf0);
__m128i res01 = _mm_shuffle_epi8(reg0, shuf1);
__m128i res10 = _mm_shuffle_epi8(reg1, shuf0);
__m128i res11 = _mm_shuffle_epi8(reg1, shuf1);
__m128i res0 = _mm_blendv_epi8(res10, res00, _mm_cmplt_epi8(shuf0, _mm_set1_epi8(16)));
__m128i res1 = _mm_blendv_epi8(res11, res01, _mm_cmplt_epi8(shuf1, _mm_set1_epi8(16)));
__m256i res = _mm256_setr_m128i(res0, res1);
return res;
}
如果您确定只使用了reg 的下半部分,那么您可以删除reg1、res10、res11 的行,并删除比较和混合。实际上,如果您没有 AVX2,则坚持使用 SSE 并使用 128 位寄存器可能会更有效。
使用 AVX2 可以显着优化一般的 32 字节混洗:
//Uses AVX2
__m256i _ext_mm256_shuffle32_epi8(__m256i reg, __m256i shuf) {
__m256i regAll0 = _mm256_permute2x128_si256(reg, reg, 0x00);
__m256i regAll1 = _mm256_permute2x128_si256(reg, reg, 0x11);
__m256i resR0 = _mm256_shuffle_epi8(regAll0, shuf);
__m256i resR1 = _mm256_shuffle_epi8(regAll1, shuf);
__m256i res = _mm256_blendv_epi8(resR1, resR0, _mm256_cmpgt_epi8(_mm256_set1_epi8(16), shuf));
return res;
}
当心:代码未经测试!