私はAVX2とFMA3を搭載したi5-4250Uを持っています。私が書いたLinux上のGCC4.8.1でいくつかの密な行列乗算コードをテストしています。以下は私がコンパイルする3つの異なる方法のリストです。
SSE2: gcc matrix.cpp -o matrix_gcc -O3 -msse2 -fopenmp
AVX: gcc matrix.cpp -o matrix_gcc -O3 -mavx -fopenmp
AVX2+FMA: gcc matrix.cpp -o matrix_gcc -O3 -march=native -fopenmp -ffast-math
SSE2バージョンとAVXバージョンは、パフォーマンスが明らかに異なります。ただし、AVX2 + FMAはAVXバージョンよりも優れています。わかりません。 FMAがないと仮定すると、CPUのピークフロップの80%以上を取得しますが、FMAを使用するとはるかにうまくいくはずだと思います。行列の乗算は、FMAから直接恩恵を受けるはずです。私は基本的にAVXで一度に8つのドット積を実行しています。 march=native
をチェックすると、次のようになります。
cc -march=native -E -v - </dev/null 2>&1 | grep cc1 | grep fma
...-march=core-avx2 -mavx -mavx2 -mfma -mno-fma4 -msse4.2 -msse4.1 ...
したがって、有効になっていることがわかります(-mfma
を追加したことを確認しますが、違いはありません)。 ffast-math
は、緩和された浮動小数点モデルを許可する必要があります SSE/AVXでFusedMultiply-Add(FMA)命令を使用する方法
編集:
Mysticialのコメントに基づいて、先に進んで_mm256_fmadd_psを使用しましたが、AVX2 + FMAバージョンの方が高速です。 コンパイラがこれを行わない理由がわかりません。現在、約80 GFLOPS(ピークフロップの110%がない場合)を取得しています。 FMA)1000x1000以上のマトリックス用。誰かが私のピークフロップ計算を信用しない場合に備えて、ここで私がしたことです。
peak flops (no FMA) = frequency * simd_width * ILP * cores
= 2.3GHZ * 8 * 2 * 2 = 73.2 GFLOPS
peak flops (with FMA) = 2 * peak flops (no FMA) = 146.2 GFLOPS
両方のコアを使用する場合のターボモードのCPUは2.3GHzです。 Ivy Bridgeは1つのAVX乗算と1つのAVX加算を同時に実行できるため、ILPで2を取得します(これを確実にするために、ループを数回展開しました)。
ピークフロップの約55%しか取得していません(FMAを使用)。理由はわかりませんが、少なくとも今何かを見ています。
副作用の1つは、信頼できる単純な行列乗算アルゴリズムと比較すると、小さなエラーが発生することです。これは、FMAの丸めモードが通常の2つではなく1つしかないためだと思います(皮肉なことに、IEEE浮動小数点ルールの方が優れているとはいえ)。
編集:
誰かがやり直す必要があります サイクルごとに理論上の最大4 FLOPを達成するにはどうすればよいですか? しかし、Haswellを使用してサイクルごとに8つの倍精度浮動小数点FLOPSを実行します。
編集
実際、MysticialはFMA3をサポートするようにプロジェクトを更新しました(上記のリンクの彼の回答を参照してください)。私は彼のコードをMSVC2012を使用してWindows8で実行しました(LinuxバージョンはFMAサポートでコンパイルされなかったため)。結果は次のとおりです。
Testing AVX Mul + Add:
Seconds = 22.7417
FP Ops = 768000000000
FLOPs = 3.37705e+010
sum = 17.8122
Testing FMA3 FMA:
Seconds = 22.1389
FP Ops = 1536000000000
FLOPs = 6.938e+010
sum = 333.309
これは、倍精度浮動小数点のFMA3の69.38GFLOPSです。単精度浮動小数点の場合は、2倍にして138.76 SP GFLOPS。ピークは146.2 SP GFLOPS。これはピークの95%です!言い換えると、GEMMコードをかなり改善できるはずです(ただし、すでにEigenよりもかなり高速です)。
ここでは、質問のごく一部にのみ回答します。 _mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0)
と書くと、gcc-4.9はインラインasmのように処理し、あまり最適化しません。 gccとclangの両方でサポートされている構文であるareg0*breg0+tmp0
に置き換えると、gccは最適化を開始し、可能な場合はFMAを使用できます。 I 改善 gcc-5の場合、たとえば_mm256_add_ps
は、単に+
を使用するインライン関数として実装されるようになったため、組み込み関数を含むコードも最適化できます。
_mm256_add_ps(_mm256_mul_ps(a, b), c)
を単一のfma命令に縮小するには、次のコンパイラオプションで十分です(例:vfmadd213ps
)。
GCC 5.3: -O2 -mavx2 -mfma
Clang 3.7: -O1 -mavx2 -mfma -ffp-contract=fast
ICC 13: -O1 -march=core-avx2
MSVCで/O2 /Arch:AVX2 /fp:fast
を試しましたが、それでも収縮しません(驚きです)。 MSVCはスカラー演算を縮小します 。
GCCは、少なくともGCC5.1以降これを開始しました。
一部のコンパイラでは、この最適化には-O1
で十分ですが、全体的なパフォーマンスには常に少なくとも-O2
を使用します、できれば-O3 -march=native -flto
と、プロファイルに基づく最適化。
また、コードに問題がない場合は、-ffast-math
。