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つは、単一のコア内でより多くのシリコンを使用しているということです。他のスレッドがサイクルを取得するのを妨げていません。私たちの計算グリッドでは、通常、あるマシンで複数のシングルスレッドプロセスを実行して、すべてのコアを常にビジー状態に保ちます。このような場合、コアを使用してこの合計を行うことは経済的ではなく、単にサイクルを盗むことになります別のスレッドが別のジョブを実行している可能性があります。
出典
2017-08-01 15:55:26
Tim
まあ、ベクトル化オプティマイザはいずれのループも最適化していない場合もありますが、試験環境では、システムの原理を理解していることを明確にするために、特に、等価浮動小数点の場合は結合性の欠如のために禁止されている可能性が高いため、最終的な最終的な折り返しを伴うベクトル加算として水平加算を実行する必要があります。配列の明示的なアライメント仕様では、コードの複雑さが原因でオプティマイザが実行されるのを防ぐことができませんでした。 – doynax
試験のために、私は、正直言って、これをターゲットアーキテクチャ用のアセンブリで手作業でコーディングしているのが好きです。このコードは、最終的には最適化されるはずです(過去にHPCのためのいくつかの同様のベクトル処理を行い、GCCからアセンブリ出力を検証しました)。 OPは間違いなくバイナリを逆アセンブルしてSIMD命令をチェックする必要があります... – madscientist159
ここでは組み込み関数があり、最適化コンパイラはコードが混乱していて不思議に思うかもしれません(逆の理由で無作為に分解します)。ここで私が気にするのは、 'float'に' int'を代入する '単精度浮動小数点値'に言及すると '' - のような恐ろしいオプションを使わない限り、蓄積のために本質的に連続的な依存関係連鎖が残っています。 ffast-math'を実行し、その過程でほとんどの数値アルゴリズムを破ります。正直なところ、並列蓄積バッファーが使われていない限り、私はその答えが検査で受け入れられるのを見ることができません。 – doynax