AVX中的AVX2 VPSHUFB仿真

ale*_*cco 6 x86 simd intrinsics avx

在AVX中,只有128位 PSHUFB

VPSHUFB xmm1, xmm2, xmm3/m128
Run Code Online (Sandbox Code Playgroud)

只有AVX2 PSHUFB才能满足整个256位AVX寄存器的要求

VPSHUFB ymm1, ymm2, ymm3/m256
Run Code Online (Sandbox Code Playgroud)

如何使用AVX内在函数有效地模拟该指令?

同样在这种特殊情况下,源只有8个元素(字节),但这些元素可以在目的地的整个32字节内移动.所以只运行2 x就没问题了PSHUFB.

我发现的一个问题VPSHUFB是它将16(0x10)视为0,只有128和up填充为零!(最高位设置)是否可以在不添加比较和屏蔽的情况下执行此操作?

stg*_*lov 8

正如@MaratDukhan注意到的那样,_mm256_shuffle_epi8(即VPSHUFB对于ymm-s)不执行完整的32字节shuffle.至于我,很可惜......

这就是为什么为了在没有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;
}
Run Code Online (Sandbox Code Playgroud)

如果你真的想要完全改组32字节寄存器,你可以按照本文的方法进行操作.每半个一半洗牌,然后将结果混合在一起.没有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;
}
Run Code Online (Sandbox Code Playgroud)

如果您肯定知道,只有下半部分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;
}
Run Code Online (Sandbox Code Playgroud)

注意:代码未经测试!