2017-07-31 21 views
0

私は試験の準備をしており、面倒な作業をしていません。だから私はこのコードを与えていて、コードをSIMD命令に変換したのかどうか疑問に思っています。SIMD命令を使用してコードをコードに変換してください

コード

int A[100000]; 
int B[100000]; 
int C=0; 

for int(i=0; i < 100000; i++) 
    C += A[i] * B[i]; 

何の残りがありませんので、我々はそれの世話をする必要はありません。また、128ビットのレジスタであると仮定して、4つの単精度浮動小数点数を計算することもできます。

マイ結果 - SIMDあなたはSIMD命令を使用しての代わりに、複数のスレッドを持つプログラムを書くために見ることができますどのような利点

int A[100000]; 
int B[100000]; 
int C=0; 

for int(i=0; i < 100000/4; i += 4) 
    C += A[i] * B[i]; 
    C += A[i+1] * B[i+1]; 
    C += A[i+2] * B[i+2]; 
    C += A[i+3] * B[i+3]; 

を使用していますか?

答えて

1

2番目のループの中カッコが省略されていると仮定すると、forループにtypoとtypoがあり、浮動小数点数を乗算することを尋ねるコードにはint配列が表示されます。コンパイラがそれを見るならば。コンパイラはAとBの4つの値をそれぞれ1つの命令として1つの命令として4つの倍数を実行することができますが、コンパイラは4つの各製品を抽出し、 SIMDレジスタからの値は通常非常に遅いです。

一方、あなたはこの

float A[100000]; 
float B[100000]; 
float C0=0, C1=0, C2=0, C3=0; 

for (size_t i=0; i < 100000/4; i += 4) 
{ 
    C0 += A[i+0] * B[i+0]; 
    C1 += A[i+1] * B[i+1]; 
    C2 += A[i+2] * B[i+2]; 
    C3 += A[i+3] * B[i+3]; 
} 
float C = (C0 + C1) + (C2 + C3); 

が次に良いコンパイラは、それが各ループ内では、2つのSIMDレジスタをロードし、それらを乗算することを見ているように、今、これをvectoriseことができなかった場合、それはに結果を追加することができます合計のSIMDレジスタであり、最後に4つの合計を抽出して合計します。

ベクトル化コンパイルではSIMDでこれを行うことができ、個々の合計の評価順序は変更されません(FP数学は結合ではありません)。コンパイラは通常、このような理由でFP演算の順序を変更することは許可されていません(技術的には言語標準に違反するような余分なフラグがないわけではありません)ので、上記のコードはSIMD命令で正確に表現でき、 (実際には、乗算はそれが立つにつれてボトルネックになるので、私はループをさらに進化させるだろう)。

これはSIMDを使った手法の一種であり、ベクトル命令でどのように演算を最適に実装するかを理解してから考える必要があります。そして、同じシーケンスの演算を実行するコードを書いてください。あなたは終わった。

または、組み込み関数を使用してベクトル命令を記述するか、OpenMPなどを使用してコンパイラに何をすべきかを明示的に伝えることができます。

このような操作のためのスレッドに対するSIMDの利点の1つは、単一のコア内でより多くのシリコンを使用しているということです。他のスレッドがサイクルを取得するのを妨げていません。私たちの計算グリッドでは、通常、あるマシンで複数のシングルスレッドプロセスを実行して、すべてのコアを常にビジー状態に保ちます。このような場合、コアを使用してこの合計を行うことは経済的ではなく、単にサイクルを盗むことになります別のスレッドが別のジョブを実行している可能性があります。

1

はい、提供されたコードは、実行可能なCPUとコンパイラを使用してSIMD命令にコンパイルする必要があります。

ベクトル対応プロセッサでは、SIMDは同一の並列計算を大幅に加速するハードウェア機能を公開します。たとえば、処理されるデータが連続したメモリ領域に配置されていると仮定すると、SIMDはストリーミングRAMアクセスのために、通常、単一コア上のキャッシュをよりよく使用します。マルチコアを使用すると、さまざまなコアが同時にデータを書き込もうとすると、キャッシュ競合およびその他の同期オーバーヘッドによって実際にパフォーマンスが低下する可能性があります。これは、von-Neumannマシンの本来の機能強化に加えて、共有システムメモリから独立した命令ではなく、4つの命令を読み出すだけで済むようになりました。

これらの算術演算を並列に行うロジックは常に存在しますが、使用するには特定のSIMD命令が必要です。結果として、SIMDは、ハンドチューニングが全体的な最適化を意味するホットループで使用される傾向があります。

+0

まあ、ベクトル化オプティマイザはいずれのループも最適化していない場合もありますが、試験環境では、システムの原理を理解していることを明確にするために、特に、等価浮動小数点の場合は結合性の欠如のために禁止されている可能性が高いため、最終的な最終的な折り返しを伴うベクトル加算として水平加算を実行する必要があります。配列の明示的なアライメント仕様では、コードの複雑さが原因でオプティマイザが実行されるのを防ぐことができませんでした。 – doynax

+0

試験のために、私は、正直言って、これをターゲットアーキテクチャ用のアセンブリで手作業でコーディングしているのが好きです。このコードは、最終的には最適化されるはずです(過去にHPCのためのいくつかの同様のベクトル処理を行い、GCCからアセンブリ出力を検証しました)。 OPは間違いなくバイナリを逆アセンブルしてSIMD命令をチェックする必要があります... – madscientist159

+0

ここでは組み込み関数があり、最適化コンパイラはコードが混乱していて不思議に思うかもしれません(逆の理由で無作為に分解します)。ここで私が気にするのは、 'float'に' int'を代入する '単精度浮動小数点値'に言及すると '' - のような恐ろしいオプションを使わない限り、蓄積のために本質的に連続的な依存関係連鎖が残っています。 ffast-math'を実行し、その過程でほとんどの数値アルゴリズムを破ります。正直なところ、並列蓄積バッファーが使われていない限り、私はその答えが検査で受け入れられるのを見ることができません。 – doynax

関連する問題