6

ループ内にいくつかのコードがあります

for(int i = 0; i < n; i++)
{
  u[i] = c * u[i] + s * b[i];
}

したがって、uとbは同じ長さのベクトルであり、cとsはスカラーです。このコードは、高速化のためにSSEで使用するためのベクトル化の良い候補ですか?

アップデート

私はベクトル化を学び(組み込み関数を使用すればそれほど難しくないことがわかりました)、SSEにループを実装しました。ただし、VC ++コンパイラでSSE2フラグを設定すると、自分のSSEコードとほぼ同じパフォーマンスが得られます。一方、Intelコンパイラは、私のSSEコードやVC++コンパイラよりもはるかに高速でした。

これが私が参考のために書いたコードです

double *u = (double*) _aligned_malloc(n * sizeof(double), 16);
for(int i = 0; i < n; i++)
{
   u[i] = 0;
}

int j = 0;
__m128d *uSSE = (__m128d*) u;
__m128d cStore = _mm_set1_pd(c);
__m128d sStore = _mm_set1_pd(s);
for (j = 0; j <= i - 2; j+=2)
{
  __m128d uStore = _mm_set_pd(u[j+1], u[j]);

  __m128d cu = _mm_mul_pd(cStore, uStore);
  __m128d so = _mm_mul_pd(sStore, omegaStore);

  uSSE[j/2] = _mm_add_pd(cu, so);
}
for(; j <= i; ++j)
{
  u[j] = c * u[j] + s * omegaCache[j];
}
4

5 に答える 5

5

はい、これはベクトル化の優れた候補です。ただし、その前に、コードのプロファイルを作成して、これが実際に最適化する価値があることを確認してください。そうは言っても、ベクトル化は次のようになります。

int i;
for(i = 0; i < n - 3; i += 4)
{
  load elements u[i,i+1,i+2,i+3]
  load elements b[i,i+1,i+2,i+3]
  vector multiply u * c
  vector multiply s * b
  add partial results
  store back to u[i,i+1,i+2,i+3]
}

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4)
for( ; i < n; i++)
  u[i] = c * u[i] + s * b[i];

さらにパフォーマンスを向上させるには、さらに配列要素をプリフェッチするか、ループを展開し、ソフトウェアパイプラインを使用して、別の反復からのメモリアクセスで1つのループの計算をインターリーブすることを検討できます。

于 2010-05-27T04:06:15.337 に答える
2

_mm_set_pdベクトル化されていません。文字通りの場合、スカラー演算を使用して2つのdoubleを読み取り、2つのスカラーdoubleを結合して、SSEレジスタにコピーします。_mm_load_pd代わりに使用してください。

于 2010-06-30T15:19:23.030 に答える
1

おそらくそうですが、いくつかのヒントでコンパイラを支援する必要があります。 __restrict__ポインターに配置すると、2つのポインターの間にエイリアスがないことをコンパイラーに通知します。ベクトルの配置がわかっている場合は、それをコンパイラーに伝えます(Visual C ++にはいくつかの機能がある場合があります)。

私自身はVisualC++に精通していませんが、ベクトル化には適さないと聞いています。代わりにインテル®コンパイラーの使用を検討してください。Intelでは、生成されたアセンブリを非常にきめ細かく制御できます:http: //www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm

于 2010-05-27T04:07:23.153 に答える
1

はい、U配列とB配列の重複がないと仮定すると、これはベクトル化の優れた候補です。ただし、コードはメモリアクセス(ロード/ストア)によって制限されます。ベクトル化はループあたりのサイクルを減らすのに役立ちますが、UおよびB配列のキャッシュミスのために命令が停止します。Intel C / C ++コンパイラは、Xeonx5500プロセッサのデフォルトフラグを使用して次のコードを生成します。コンパイラはループを8だけ展開し、xmm [0-16]SIMDレジスタを使用してSIMDADD(addpd)およびMULTIPLY(mulpd)命令を使用します。各サイクルで、プロセッサは2つのSIMD命令を発行して、レジスタにデータの準備ができていると仮定して、4ウェイスカラーILPを生成できます。

ここで、U、B、C、およびSは倍精度(8バイト)です。

    ..B1.14:                        # Preds ..B1.12 ..B1.10
    movaps    %xmm1, %xmm3                                  #5.1
    unpcklpd  %xmm3, %xmm3                                  #5.1
    movaps    %xmm0, %xmm2                                  #6.12
    unpcklpd  %xmm2, %xmm2                                  #6.12
      # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3
    ..B1.15:     # Preds ..B1.15 ..B1.14
    movsd     (%rsi,%rcx,8), %xmm4                          #6.21
    movhpd    8(%rsi,%rcx,8), %xmm4                         #6.21
    mulpd     %xmm2, %xmm4                                  #6.21
    movaps    (%rdi,%rcx,8), %xmm5                          #6.12
    mulpd     %xmm3, %xmm5                                  #6.12
    addpd     %xmm4, %xmm5                                  #6.21
    movaps    16(%rdi,%rcx,8), %xmm7                        #6.12
    movaps    32(%rdi,%rcx,8), %xmm9                        #6.12
    movaps    48(%rdi,%rcx,8), %xmm11                       #6.12
    movaps    %xmm5, (%rdi,%rcx,8)                          #6.3
    mulpd     %xmm3, %xmm7                                  #6.12
    mulpd     %xmm3, %xmm9                                  #6.12
    mulpd     %xmm3, %xmm11                                 #6.12
    movsd     16(%rsi,%rcx,8), %xmm6                        #6.21
    movhpd    24(%rsi,%rcx,8), %xmm6                        #6.21
    mulpd     %xmm2, %xmm6                                  #6.21
    addpd     %xmm6, %xmm7                                  #6.21
    movaps    %xmm7, 16(%rdi,%rcx,8)                        #6.3
    movsd     32(%rsi,%rcx,8), %xmm8                        #6.21
    movhpd    40(%rsi,%rcx,8), %xmm8                        #6.21
    mulpd     %xmm2, %xmm8                                  #6.21
    addpd     %xmm8, %xmm9                                  #6.21
    movaps    %xmm9, 32(%rdi,%rcx,8)                        #6.3
    movsd     48(%rsi,%rcx,8), %xmm10                       #6.21
    movhpd    56(%rsi,%rcx,8), %xmm10                       #6.21
    mulpd     %xmm2, %xmm10                                 #6.21
    addpd     %xmm10, %xmm11                                #6.21
    movaps    %xmm11, 48(%rdi,%rcx,8)                       #6.3
    addq      $8, %rcx                                      #5.1
    cmpq      %r8, %rcx                                     #5.1
    jl        ..B1.15       # Prob 99%                      #5.1
于 2011-12-20T15:53:23.737 に答える
-2

uとbをメモリにどのように配置したかによって異なります。両方のメモリブロックが互いに離れている場合、このシナリオではSSEはあまりブーストしません。

配列uとbは、SOA(配列の構造)ではなくAOE(構造の配列)であることが推奨されます。これは、両方を1つの命令でレジスタにロードできるためです。

于 2010-05-27T03:52:42.623 に答える