C 将a _m128i移位n位

C 将a _m128i移位n位,c,x86,sse,simd,sse2,C,X86,Sse,Simd,Sse2,我有一个\uuum128i变量,需要将其128位的n位值移位,即像\u mm\u srli\u si128和\u mm\u slli\u si128一样工作,但在位而不是字节上工作。最有效的方法是什么?这是我为SSE2的左/右即时换档所能想到的最好方法: #include <stdio.h> #include <emmintrin.h> #define SHL128(v, n) \ ({ \ __m128i v1, v2; \ \ if ((n) &g

我有一个
\uuum128i
变量,需要将其128位的n位值移位,即像
\u mm\u srli\u si128
\u mm\u slli\u si128
一样工作,但在位而不是字节上工作。最有效的方法是什么?

这是我为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;
}
请注意,SHL128/SHR128宏是使用gcc、clang和其他一些编译器支持的gcc扩展实现的,但是如果您的编译器不支持此扩展,则需要对其进行调整

还请注意,测试工具中使用的SIMD类型的printf扩展适用于Apple gcc、clang等,但如果编译器不支持此功能,并且您希望测试代码,则需要实现自己的SIMD打印例程


关于性能的注意事项-只要
n
是编译时常数(对于shift intrinsic,它无论如何都需要是该常数),if/else分支将得到优化,因此对于n>=64的情况有2条指令,对于n<64的情况有4条指令。

可能重复@Salgar:no,这不是重复-这个问题是关于移位整个128位向量的,而你引用的dupe是关于元素移位的。你只需查看调试器中的值,或者您可以为SIMD数据类型编写一些调试打印实用程序-如果您打算参与编写SSE代码,它们可能会很有用。我想与您分享我的n>64位版本:
#define SHL(v,n)({\register uuum128i x;register int m;\if(n>64){x=mm_SLLI128(v,8);\m=n-64;}else{x=v;m=n;}\register\uuum128i v1=\umm\uslli\uepi64(x,m);\register\uuum128i v2=\umm\uslli\usi128(x,8);\v2=\umm\usrli\uepi64(v2,64-(m))\v1=\umm\u或\usi128(v1,v2);\)
可以改进吗?在注释中阅读代码并不容易-也许你可以将其作为一个新问题发布?@Alex:你的
m128i\u to\u bitstr
功能可能有问题吗?您正在使用
uint16\u t*
索引到
\uu m128i
中,但将每个16位元素视为
std::bitset
?也许您应该将其索引为16 x
uint8\t
?(我还没有仔细看这个,所以如果我错过了什么,很抱歉。)@Paul R谢谢!是的,您的班次代码正确,我的打印代码有错误,现在一切正常:
$ 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
$