2

説明不足の質問で申し訳ありません。もう一度質問を言い換えさせてください:

セットアップ:

SSEを使用して、ADDと、4つの配列からの4つの32ビット値のビット単位の操作を同時に実行する必要があります。これら 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

-msse2gcc で SSE コードをコンパイルするには、フラグが必要です。

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

他の要件については、上記の説明からはあまり明確ではないため、ベクトル化する対象の作業 (スカラー) サンプル コードを投稿できるとよいでしょう。

于 2012-11-29T21:07:08.863 に答える