对4个整数大小的数组进行SSE操作

时间:2012-11-29 21:01:42

标签: c assembly sse simd intrinsics

对于之前的非描述性问题,我们深表歉意。请允许我再次重新提出问题:

设置:

我需要使用SSE同时对来自4个阵列的4个32位值进行ADD和一些有点操作。这4个数组中的所有元素都是整数(32位)。结果将转到第5个阵列。

所以我的问题是:

  1. 我需要包含哪些头文件和编译器标志,以便我可以使用C?
  2. 运行SSE
  3. 保罗提供的示例代码是否仍有效?
  4. 另一个问题,如果我需要从整数A中读取最后一位,从整数B读取第一位,并将整数C中的最后一位和第一位替换为刚刚读取的值,我可以在这里使用SSE吗?或者有任何快速的方法吗?在正常情况下,而不是3次访问?

    保罗提供的代码

    #include <stdint.h>
    #include <emmintrin.h>
    
    const size_t N = 4096;  // size of input/output arrays
    
    int32_t array0[N];      // 4 x input arrays
    int32_t array1[N];
    int32_t array2[N];
    int32_t array3[N];
    int32_t array_sum[N];   // output array
    
    for (size_t i = 0; i < N; i += 4)
    {
        __m128i v0 = _mm_load_si128(&array0[i]); // load 4 x vectors of 4 x int
        __m128i v1 = _mm_load_si128(&array1[i]);
        __m128i v2 = _mm_load_si128(&array2[i]);
        __m128i v3 = _mm_load_si128(&array3[i]);
        __m128i vsum = _mm_add_epi32(v0, v1);    // sum vectors
        __m128i vsum = _mm_add_epi32(vsum, v2);
        __m128i vsum = _mm_add_epi32(vsum, v3);
        _mm_store_si128(&array_out[i], vsum);    // store sum
    }
    

2 个答案:

答案 0 :(得分:9)

你所拥有的代码确实会有所改变。

#include <stdint.h>
#include <emmintrin.h>

const size_t N = 4096;  // size of input/output arrays

__declspec(align(16)) int32_t array0[N];      // 4 x input arrays
__declspec(align(16)) int32_t array1[N];
__declspec(align(16)) int32_t array2[N];
__declspec(align(16)) int32_t array3[N];
__declspec(align(16)) int32_t array_sum[N];   // output array

for (size_t i = 0; i < N; i += 4)
{
    __m128i v0 = _mm_load_si128(&array0[i]); // load 4 x vectors of 4 x int
    __m128i v1 = _mm_load_si128(&array1[i]);
    __m128i v2 = _mm_load_si128(&array2[i]);
    __m128i v3 = _mm_load_si128(&array3[i]);
    __m128i vsum = _mm_add_epi32(v0, v1);    // sum vectors
    __m128i vsum = _mm_add_epi32(vsum, v2);
    __m128i vsum = _mm_add_epi32(vsum, v3);
    _mm_store_si128(&array_sum[i], vsum);    // store sum
}

在使用内在函数时,我喜欢包含<immintrin.h>并使用gcc -march=native进行编译。这样可以访问当前硬件上实际可用的所有指令集扩展。

在另一个问题上,是的,你当然可以这样做,但只有在整数A,B和C的数组上才能有效。

示例:

__declspec(align(16)) int32_t A[N]; // input arrays
__declspec(align(16)) int32_t B[N];
__declspec(align(16)) int32_t C[N];
__declspec(align(16)) int32_t R[N]; // output array

__m128i* As = (__m128i*)A; // cast them to SSE type, avoids separate load/store calls later
__m128i* Bs = (__m128i*)B;
__m128i* Cs = (__m128i*)C;
__m128i* Rs = (__m128i*)R;

__m128i A_mask = _mm_set1_epi32(1<<31); // select these bits from A, B, and C
__m128i B_mask = _mm_set1_epi32(1);
__m128i C_mask = _mm_set1_epi32(0xffffffff ^ ( 1<<31 | 1 ));

for (size_t i = 0; i < N / 4; i ++)
{
    __m128i a = _mm_and_si128(A_mask, As[i]);
    __m128i b = _mm_and_si128(B_mask, Bs[i]);
    __m128i c = _mm_and_si128(C_mask, Cs[i]);
    Rs[i] = _mm_or_si128( _mm_or_si128(a, b), c );
}

如上所述,将int32_t数组别名化为__m128i并不是更有效,如果编译器很好,它应该编译成完全相同的代码,但它会导致更简洁的代码。推荐:))

答案 1 :(得分:2)

要使用gcc编译SSE代码,您只需要-msse2标志,例如

$ gcc -Wall -O3 -msse2 foo.c -o foo

至于其他要求,最好是你可以发布你想要矢量化的工作(标量)示例代码,因为从上面的描述中不是很清楚。