[解決済み] BLASはどうしてあんなに高性能なのか?
質問
好奇心から、私は自分自身の行列乗算関数とBLAS実装のベンチマークを取ることにしました... 私は控えめに言っても、その結果に驚きました。
カスタム実装、10回の試行 1000x1000 の行列の乗算を 10 回試行しました。
Took: 15.76542 seconds.
BLASの実装、10回の試行で 1000x1000の行列の乗算を10回試行。
Took: 1.32432 seconds.
これは単精度浮動小数点数を使用しています。
私の実装です。
template<class ValT>
void mmult(const ValT* A, int ADim1, int ADim2, const ValT* B, int BDim1, int BDim2, ValT* C)
{
if ( ADim2!=BDim1 )
throw std::runtime_error("Error sizes off");
memset((void*)C,0,sizeof(ValT)*ADim1*BDim2);
int cc2,cc1,cr1;
for ( cc2=0 ; cc2<BDim2 ; ++cc2 )
for ( cc1=0 ; cc1<ADim2 ; ++cc1 )
for ( cr1=0 ; cr1<ADim1 ; ++cr1 )
C[cc2*ADim2+cr1] += A[cc1*ADim1+cr1]*B[cc2*BDim1+cc1];
}
2つ質問があります。
- 行列と行列の乗算、例えば nxm * mxn が n*n*m 回の乗算を必要とするとすると、上記のケースでは 1000^3 または 1e9 回の演算が必要です。私の2.6Ghzのプロセッサで、BLASが10*1e9の演算を1.32秒で行うことは可能なのでしょうか?乗算が1つの演算で、他に何もしていないとしても、~4秒かかるはずです。
- なぜ私の実装はこれほど遅いのですか?
どのように解決するのですか?
良い出発点は、偉大な本である 行列計算のプログラミングの科学 Robert A. van de Geijn と Enrique S. Quintana-Ortí によるものです。Quintana-Ortí です。 無料ダウンロード版もあります。
BLASは3つのレベルに分かれています。
-
レベル1では、ベクトルに対してのみ演算する線形代数関数のセットを定義しています。 これらの関数はベクトル化(例えばSSEの使用)から利益を得ます。
-
レベル2関数は、行列-ベクトル演算、例えば、いくつかの行列-ベクトル積です。 これらの関数は、レベル1関数で実装することができます。 しかし、共有メモリを持つ何らかのマルチプロセッサアーキテクチャを利用した専用の実装を提供できれば、この関数の性能を高めることができます。
-
レベル3関数は、行列と行列の積のような演算です。 ここでもレベル2関数で実装することができます。 しかし、レベル3関数はO(N^2)個のデータに対してO(N^3)個の演算を実行します。 ですから、もしあなたのプラットフォームがキャッシュ階層を持つなら、以下のような専用の実装を提供すれば、性能を向上させることができます。 キャッシュ最適化/キャッシュフレンドリー . これについては、この本でうまく説明されています。 Level3 関数の主なブーストはキャッシュの最適化から生まれます。 このブーストは、並列処理やその他のハードウェア最適化による2番目のブーストを大幅に上回ります。
ところで、高性能な BLAS 実装のほとんど (あるいはすべて) は Fortran で実装されているわけではありません。 ATLAS は C で実装され、GotoBLAS/OpenBLAS は C で、その性能的に重要な部分は Assembler で実装されています。 BLASのリファレンス実装のみがFortranで実装されています。 しかし、これらのBLASの実装はすべて、LAPACKとリンクできるようにFortranインターフェースを提供しています(LAPACKはBLASからすべての性能を獲得しています)。
最適化されたコンパイラーはこの点で小さな役割を果たします(そして GotoBLAS/OpenBLAS ではコンパイラーはまったく重要ではありません)。
IMHOでは、Coppersmith-WinogradアルゴリズムやStrassenアルゴリズムのようなアルゴリズムを使用するBLAS実装はありません。 考えられる理由は
- これらのアルゴリズムのキャッシュ最適化された実装を提供することが不可能なため (つまり、勝つことよりも失うことの方が多い)。
- これらのアルゴリズムは数値的に安定していません。 BLAS は LAPACK の計算カーネルであるため、これはダメなことです。
- これらのアルゴリズムは、紙面上では良い時間複雑性を持っていますが、Big O記法は大きな定数を隠しているので、非常に大きな行列に対してのみ実行可能になり始めます。
編集・更新
このトピックのための新しい画期的なペーパーは BLIS論文 . これらは非常によく書かれています。 私の講義「ハイパフォーマンス・コンピューティングのためのソフトウェア基礎」では、彼らの論文に従って、行列積を実装しました。 実は、この行列積のいくつかのバリエーションを実装しました。 最も単純なものは、すべてプレーンなC言語で書かれており、コード行数は450行未満である。 他のバージョンは、ループを最適化するだけである。
for (l=0; l<MR*NR; ++l) {
AB[l] = 0;
}
for (l=0; l<kc; ++l) {
for (j=0; j<NR; ++j) {
for (i=0; i<MR; ++i) {
AB[i+j*MR] += A[i]*B[j];
}
}
A += MR;
B += NR;
}
行列-行列積の全体的な性能 だけ はこれらのループに依存します。 約99.9%の時間がここに費やされています。 他のバリエーションでは、パフォーマンスを向上させるために、イントリンシックとアセンブラコードを使用しました。 このチュートリアルでは、すべてのバリエーションを見ることができます。
ulmBLAS: GEMM (行列-行列積)のチュートリアル
BLISの論文と合わせると、Intel MKLのようなライブラリがなぜこのようなパフォーマンスを得られるのか、かなり簡単に理解できるようになります。そして、行または列の主要なストレージを使用するかどうかは、なぜ重要ではないのでしょうか!
最終的なベンチマークはこちらです (私たちはこのプロジェクトを ulmBLAS と呼んでいます)。
ulmBLAS、BLIS、MKL、openBLAS、Eigenのベンチマークです。
もう一つの編集・更新。
BLASが連立一次方程式の解のような数値線形代数問題にどのように使用されるかについてのチュートリアルも書きました。
(このLU因数分解は、例えばMatlabで連立一次方程式の解法に使われます)。
のようなLU因数分解の高度にスケーラブルな並列実装を実現する方法を説明し実証するために、このチュートリアルを拡張する時間があればと思います。
PLASMA
.
よし、これでいいんだ。 キャッシュ最適化並列LU因数分解のコード化
追記: uBLASの性能向上に関する実験も行いました。 実はuBLASの性能を上げるのはとても簡単です(言葉遊びです :)。
uBLASの実験 .
ここでは、同様のプロジェクトで BLAZE :
関連
-
[解決済み】coutはstdのメンバではない
-
[解決済み] プロセスがSIGABRT(シグナル6)を取得するのはどんなときですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み] CまたはC++を使用して、ディレクトリ内のファイルのリストを取得するにはどうすればよいですか?
-
[解決済み] C++で現在の時刻と日付を取得する方法は?
-
[解決済み] C++でPOSIXを使ってコマンドを実行し、その出力を得るにはどうしたらよいですか?
-
[解決済み] コンパイル・リンクはどのように行われるのですか?
-
[解決済み】モダンC++はタダで性能を手に入れられる?
-
[解決済み】__attribute__((constructor))は具体的にどのように機能するのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++
-
[解決済み】なぜMATLABは行列の乗算が速いのか?