【问题标题】:AVX2 VPSHUFB emulation in AVXAVX 中的 AVX2 VPSHUFB 仿真
【发布时间】:2015-12-08 14:15:10
【问题描述】:

在 AVX 中只有 128 位 PSHUFB

VPSHUFB xmm1, xmm2, xmm3/m128

只有 AVX2 具有完整的 PSHUFB 用于整个 256 位 AVX 寄存器

VPSHUFB ymm1, ymm2, ymm3/m256

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

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

我发现VPSHUFB 的一个问题是将 16 (0x10) 视为 0,只有 128 及以上的值被零填充! (最高位设置)是否可以在不添加比较和屏蔽的情况下做到这一点?

【问题讨论】:

  • 可能你没有注意到,但 AVX2 VPSHUFB ymm, ymm, ymm/m256 并不是真正的 256-bit shuffle,而是 2x 128-bit shuffle。
  • 有趣。谢谢!

标签: x86 simd intrinsics avx


【解决方案1】:

正如@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 的下半部分,那么您可以删除reg1res10res11 的行,并删除比较和混合。实际上,如果您没有 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;
}

当心:代码未经测试!

【讨论】:

  • 回答得很透彻,比我预期的要多得多。非常感谢!
  • 您是否知道您的仅 AVX 实现包含仅在 AVX2 中可用的 _mm256_extracti128_si256
  • @stgatilov 再检查一遍,_mm256_extracti128_si256 编译成vextracti128,也就是AVX2。你说的是_mm256_extractf128_si256,它编译为vextractf128。它们相似,但 vextractf128 在浮点域上运行,vextracti128 在整数域上运行。
  • @placel:抱歉,我没有注意到我使用了两种不同的提取方式。希望我现在已经解决了这个问题。
  • 我已经在我的代码中的很多地方测试了_ext_mm256_shuffle32_epi8(),它运行良好。经常使用__m256is 的任何人都必须拥有。
猜你喜欢
  • 1970-01-01
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 2017-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多