c - 移动 n 位的 __m128i

标签 c x86 sse simd sse2

我有一个 __m128i 变量,我需要将它的 128 位值移动 n 位,即像 _mm_srli_si128_mm_slli_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;
}

测试:

$ 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、et al,但如果您的编译器不支持它并且您想要测试代码您需要实现自己的 SIMD 打印例程。

关于性能的注意事项 - 只要 n 是一个编译时常量(对于移位内在函数它无论如何都需要),if/else 分支将得到优化,所以你有 2 条指令对于 n >= 64 的情况,对于 n < 64 的情况有 4 条指令。

关于c - 移动 n 位的 __m128i,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17610696/

相关文章:

assembly - 为什么我不能改变段寄存器的值? (MASM)

multithreading - 这个 CMPXCHG16B 指令的仿真有什么问题?

assembly - SSE:如何将每个打包字节的符号位提取到打包寄存器中?

debugging - SSE2 : _mm_mul_ps fails on OS X in case of GCC 4. 2 和 O0 优化

c - 返回未初始化的、最终未使用的结构是否是未定义的行为?

c - 如何删除一个数组结构体,并且最后一个索引不能被其他索引替换?

c - 求和对于 3 的幂是正确的,但除此之外

c - 混合 OpenMP+MPI : I need an explanation from this example

c++ - boost::random::uniform_real_distribution 应该在处理器之间相同吗?

c++ - AVX2 赢家通吃差异搜索