2

抱歉之前的非描述性问题。请允许我再次改述这个问题:

设置:

我需要使用 SSE 同时对 4 个数组中的 4 个 32 位值进行 ADD 和一些位操作。这 4 个数组中的所有元素都是整数大小(32 位)。结果进入第 5 个数组。

所以我的问题是:

  1. 我需要包含哪些头文件和编译器标志才能使用 C 运行 SSE?
  2. Paul 提供的示例代码是否仍然有效?

另一个问题,如果我需要从整数 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
}
4

2 回答 2

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 效率并不高,如果编译器不错,它应该编译成完全相同的代码,但它会产生更少冗长的代码。受到推崇的 :)

于 2012-11-30T07:40:29.197 回答
2

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

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

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

于 2012-11-29T21:07:08.863 回答