7

在 SSE3 中,PAligNR 指令执行以下操作:

PARIGNR 将目标操作数(第一个操作数)和源操作数(第二个操作数)连接成一个中间组合,将组合以字节粒度向右移动一个常量立即数,并将右对齐的结果提取到目标中。

我目前正在移植我的 SSE4 代码以使用 AVX2 指令并使用 256 位寄存器而不是 128 位。天真地,我相信内在函数(VALIGNR)执行与仅在 256 位寄存器上_mm256_alignr_epi8相同的操作。_mm_alignr_epi8然而可悲的是,情况并非如此。实际上,_mm256_alignr_epi8将 256bit 寄存器视为 2 个 128bit 寄存器,对相邻的两个 128bit 寄存器执行 2 次“对齐”操作。_mm_alignr_epi8一次有效地执行与 2 个寄存器相同的操作。在这里最清楚地说明了这一点:_mm256_alignr_epi8

目前我的解决方案是_mm_alignr_epi8通过将 ymm(256 位)寄存器分成两个 xmm(128 位)寄存器(高和低)来继续使用,如下所示:

__m128i xmm_ymm1_hi = _mm256_extractf128_si256(ymm1, 0);
__m128i xmm_ymm1_lo = _mm256_extractf128_si256(ymm1, 1);
__m128i xmm_ymm2_hi = _mm256_extractf128_si256(ymm2, 0);
__m128i xmm_ymm_aligned_lo = _mm_alignr_epi8(xmm_ymm1_lo, xmm_ymm1_hi, 1);
__m128i xmm_ymm_aligned_hi = _mm_alignr_epi8(xmm_ymm2_hi, xmm_ymm1_lo, 1);
__m256i xmm_ymm_aligned = _mm256_set_m128i(xmm_ymm_aligned_lo, xmm_ymm_aligned_hi);

这行得通,但必须有更好的方法,对吧?是否应该使用更“通用”的 AVX2 指令来获得相同的结果?

4

3 回答 3

5

你用palignr什么?如果只是为了处理数据错位,只需使用未对齐的负载即可;它们在现代英特尔 µ 架构上通常“足够快”(并且会为您节省大量代码大小)。

如果您palignr出于其他原因需要类似行为,您可以简单地利用未对齐的负载支持以无分支的方式执行此操作。除非您完全受负载存储限制,否则这可能是首选的习惯用法。

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n)
{
    // Do whatever your compiler needs to make this buffer 64-byte aligned.
    // You want to avoid the possibility of a page-boundary crossing load.
    char buffer[64];

    // Two aligned stores to fill the buffer.
    _mm256_store_si256((__m256i *)&buffer[0], v0);
    _mm256_store_si256((__m256i *)&buffer[32], v1);

    // Misaligned load to get the data we want.
    return _mm256_loadu_si256((__m256i *)&buffer[n]);
}

如果你能提供更多关于你如何使用的信息palignr,我可能会更有帮助。

于 2011-12-26T16:29:21.757 回答
3

我能想出的唯一解决方案是:

static inline __m256i _mm256_alignr_epi8(const __m256i v0, const __m256i v1, const int n)
{
  if (n < 16)
  {
    __m128i v0h = _mm256_extractf128_si256(v0, 0);
    __m128i v0l = _mm256_extractf128_si256(v0, 1);
    __m128i v1h = _mm256_extractf128_si256(v1, 0);
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n);
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n);
    __m256i vout = _mm256_set_m128i(voutl, vouth);
    return vout;
  }
  else
  {
    __m128i v0h = _mm256_extractf128_si256(v0, 1);
    __m128i v0l = _mm256_extractf128_si256(v1, 0);
    __m128i v1h = _mm256_extractf128_si256(v1, 1);
    __m128i vouth = _mm_alignr_epi8(v0l, v0h, n - 16);
    __m128i voutl = _mm_alignr_epi8(v1h, v0l, n - 16);
    __m256i vout = _mm256_set_m128i(voutl, vouth);
    return vout;
  }
}

我认为这与您的解决方案几乎相同,除了它还处理 >= 16 字节的移位。

于 2011-12-15T09:53:44.123 回答
3

我们需要 2 条指令:“vperm2i128”和“vpalignr”来将“palignr”扩展为 256 位。

请参阅:https ://software.intel.com/en-us/blogs/2015/01/13/programming-using-avx2-permutations

于 2015-08-10T19:25:02.410 回答