我有一个__m128i
变量,我需要移动它的 128 位值 n 位,即喜欢_mm_srli_si128
和_mm_slli_si128
工作,但在位而不是字节上。这样做最有效的方法是什么?
问问题
3858 次
1 回答
17
对于 SSE2 的左/右立即移位,这是我能想到的最好的方法:
#include <stdio.h>
#include <emmintrin.h>
#define SHL128(v, n) \
({ \
__m128i v1, v2; \
\
if ((n) >= 64) \
{ \
v1 = _mm_slli_si128(v, 8); \
v1 = _mm_slli_epi64(v1, (n) - 64); \
} \
else \
{ \
v1 = _mm_slli_epi64(v, n); \
v2 = _mm_slli_si128(v, 8); \
v2 = _mm_srli_epi64(v2, 64 - (n)); \
v1 = _mm_or_si128(v1, v2); \
} \
v1; \
})
#define SHR128(v, n) \
({ \
__m128i v1, v2; \
\
if ((n) >= 64) \
{ \
v1 = _mm_srli_si128(v, 8); \
v1 = _mm_srli_epi64(v1, (n) - 64); \
} \
else \
{ \
v1 = _mm_srli_epi64(v, n); \
v2 = _mm_srli_si128(v, 8); \
v2 = _mm_slli_epi64(v2, 64 - (n)); \
v1 = _mm_or_si128(v1, v2); \
} \
v1; \
})
int main(void)
{
__m128i va = _mm_setr_epi8(0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f);
__m128i vb, vc;
vb = SHL128(va, 4);
vc = SHR128(va, 4);
printf("va = %02vx\n", va);
printf("vb = %02vx\n", vb);
printf("vc = %02vx\n", vc);
printf("\n");
vb = SHL128(va, 68);
vc = SHR128(va, 68);
printf("va = %02vx\n", va);
printf("vb = %02vx\n", vb);
printf("vc = %02vx\n", vc);
return 0;
}
测试:
$ gcc -Wall -msse2 shift128.c && ./a.out
va = 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
vb = 00 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0
vc = 10 20 30 40 50 60 70 80 90 a0 b0 c0 d0 e0 f0 00
va = 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
vb = 00 00 00 00 00 00 00 00 00 10 20 30 40 50 60 70
vc = 90 a0 b0 c0 d0 e0 f0 00 00 00 00 00 00 00 00 00
$
请注意,SHL128/SHR128 宏是使用 gcc、clang 和其他一些编译器支持的 gcc 扩展实现的,但如果您的编译器不支持此扩展,则需要调整这些宏。
另请注意,测试工具中使用的 SIMD 类型的 printf 扩展适用于 Apple gcc、clang等,但如果您的编译器不支持此功能并且您想要测试代码,则需要实现自己的 SIMD 打印例行公事。
关于性能的注意事项 - if/else 分支将得到优化,只要n
是编译时常量(对于移位内在函数它无论如何都需要),因此对于 n >= 64 情况有 2 条指令和 4 条指令n < 64 的情况。
于 2013-07-12T09:03:19.700 回答