14

SSE組み込み関数を扱うのはこれが初めてです。Intel SSE組み込み型(SSE4.2まで)を使用して、単純なコードをより高速なバージョンに変換しようとしています。いくつかのエラーが発生したようです。

コードのスカラーバージョンは次のとおりです:(単純な行列の乗算)

     void mm(int n, double *A, double *B, double *C)
     {
        int i,j,k;
        double tmp;

        for(i = 0; i < n; i++)
            for(j = 0; j < n; j++) {
                    tmp = 0.0;
                    for(k = 0; k < n; k++)
                            tmp += A[n*i+k] *
                                   B[n*k+j];
                    C[n*i+j] = tmp;

              }
            }

これは私のバージョンです:私は含まれています#include <ia32intrin.h>

      void mm_sse(int n, double *A, double *B, double *C)
      {
        int i,j,k;
        double tmp;
        __m128d a_i, b_i, c_i;

        for(i = 0; i < n; i++)
            for(j = 0; j < n; j++) {
                    tmp = 0.0;
                    for(k = 0; k < n; k+=4)
                            a_i = __mm_load_ps(&A[n*i+k]);
                            b_i = __mm_load_ps(&B[n*k+j]);
                            c_i = __mm_load_ps(&C[n*i+j]);

                            __m128d tmp1 = __mm_mul_ps(a_i,b_i);
                            __m128d tmp2 = __mm_hadd_ps(tmp1,tmp1);
                            __m128d tmp3 = __mm_add_ps(tmp2,tmp3);
                            __mm_store_ps(&C[n*i+j], tmp3);

            }
         }

私はこれでどこが間違っているのですか?次のようなエラーが発生します。

mm_vec.c(84):エラー:タイプ「int」の値をタイプ「__m128d」のエンティティに割り当てることはできませんa_i = __mm_load_ps(&A [n * i + k]);

これが私がコンパイルしている方法です:icc -O2 mm_vec.c -o vec

誰かがこのコードを正確に変換するのを手伝ってくれませんか。ありがとう!

アップデート:

あなたの提案によると、私は次の変更を加えました:

       void mm_sse(int n, float *A, float *B, float *C)
       {
         int i,j,k;
         float tmp;
         __m128 a_i, b_i, c_i;

         for(i = 0; i < n; i++)
            for(j = 0; j < n; j++) {
                    tmp = 0.0;
                    for(k = 0; k < n; k+=4)
                            a_i = _mm_load_ps(&A[n*i+k]);
                            b_i = _mm_load_ps(&B[n*k+j]);
                            c_i = _mm_load_ps(&C[n*i+j]);

                            __m128 tmp1 = _mm_mul_ps(a_i,b_i);
                            __m128 tmp2 = _mm_hadd_ps(tmp1,tmp1);
                            __m128 tmp3 = _mm_add_ps(tmp2,tmp3);
                            _mm_store_ps(&C[n*i+j], tmp3);


            }
        }

しかし、今はセグメンテーション違反が発生しているようです。おそらく、配列A、B、Cの配列添え字に適切にアクセスしていないため、これを知っています。私はこれに非常に慣れていないので、これをどのように進めるかわかりません。

このコードを処理するための正しいアプローチを決定するのを手伝ってください。

4

2 に答える 2

10

表示されているエラーは、関数名にアンダースコアが多すぎるためです。例:

__mm_mul_ps

する必要があります:

_mm_mul_ps // Just one underscore up front

したがって、Cコンパイラはint、宣言が表示されていないため、返されると想定しています。

これ以外にも問題があります。同じ命令のdoubleとsingleのfloatバリアントへの呼び出しが混在しているようです。

たとえば、次のようになります。

__m128d a_i, b_i, c_i;

しかし、あなたは電話します:

__mm_load_ps(&A[n*i+k]);

__m128これはではないを返します__m128d-あなたが呼び出したかった:

_mm_load_pd

代わりは。ダブルスのペアで動作させたい場合は、他の手順についても同様です。


原因不明のセグメンテーション違反が発生し、SSEコードでメモリアライメントの問題が発生していると推測する傾向があります。つまり、SSE組み込み関数(ほとんどの場合1)に渡されるポインタは16バイトアライメントである必要があります。これは、コード内の単純なアサーションで確認するか、デバッガーで確認できます(適切に配置されている場合、ポインターの最後の桁は0になると予想されます)。

正しく位置合わせされていない場合は、位置合わせされていることを確認する必要があります。new/で割り当てられていないものについてmalloc()は、コンパイラ拡張(gccなど)を使用してこれを行うことができます。

float a[16] __attribute__ ((aligned (16)));

ご使用のバージョンのgccに、これをサポートするのに十分な大きさの最大アライメントと、スタックアライメントに関するその他のいくつかの警告がある場合。posix_memalign動的に割り当てられたストレージの場合、たとえば適切なストレージを割り当てるために、プラットフォーム固有の拡張機能を使用する必要があります。

float *a=NULL;
posix_memalign(&a, __alignof__(__m128), sizeof(float)*16);

(C ++ 11でこれを行うためのより良い、ポータブルな方法があるかもしれないと思いますが、私はまだそれについて100%確信していません)。

1位置合わせされていないロードとストアを実行できるようにする手順がいくつかありますが、それらは位置合わせされたロードに比べて非常に遅く、可能な限り避ける価値があります。

于 2012-06-08T17:07:25.450 に答える
3

ロードとストアが常に16バイトに整列されたアドレスにアクセスしていることを確認する必要があります。または、何らかの理由でこれを保証できない場合は、/の代わりに/を使用_mm_loadu_ps_mm_storeu_psます_mm_load_ps_mm_store_psこれは効率が低下しますが、アドレスがずれていてもクラッシュしません。

于 2012-06-08T19:31:55.347 に答える