在SSE3中,PALIGNR指令执行以下操作:
PALIGNR将目标操作数(第一个操作数)和源操作数(第二个操作数)连接成一个中间复合,将复合体以字节粒度向右移动一个常量立即数,并将右对齐结果提取到目的地。
我目前正在移植我的SSE4代码以使用AVX2指令并使用256位寄存器而不是128位寄存器。
天真地,我相信内在函数_mm256_alignr_epi8
(VPALIGNR)仅在256位寄存器上执行与_mm_alignr_epi8
相同的操作。但遗憾的是,事实并非如此。事实上,_mm256_alignr_epi8
将256位寄存器视为2个128位寄存器,并在两个相邻的128位寄存器上执行2次“对齐”操作。有效地执行与_mm_alignr_epi8
相同的操作,但同时在2个寄存器上执行。这里最清楚地说明了这一点:_mm256_alignr_epi8
目前我的解决方案是继续使用_mm_alignr_epi8
将ymm(256bit)寄存器分成两个xmm(128bit)寄存器(高和低),如下所示:
__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指令应该用于获得相同的结果?
答案 0 :(得分:4)
你在使用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
的更多信息,我可能会更有帮助。
答案 1 :(得分:3)
我们需要2条指令:“vperm2i128”和“vpalignr”以256位扩展“palignr”。
请参阅:https://software.intel.com/en-us/blogs/2015/01/13/programming-using-avx2-permutations
答案 2 :(得分:2)
我能想出的唯一解决方案是:
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;
}
}
我认为它与你的解决方案非常相似,除了它还处理&gt; = 16字节的移位。