web-dev-qa-db-ja.com

ブースト行列乗算が私のものより遅いのはなぜですか?

boost::numeric::ublas::matrixを使用して1つの行列乗算を実装しました( my full、working boost code を参照)

Result result = read ();

boost::numeric::ublas::matrix<int> C;
C = boost::numeric::ublas::prod(result.A, result.B);

標準アルゴリズムを使用した別のアルゴリズム( 完全な標準コード を参照):

vector< vector<int> > ijkalgorithm(vector< vector<int> > A, 
                                    vector< vector<int> > B) {
    int n = A.size();

    // initialise C with 0s
    vector<int> tmp(n, 0);
    vector< vector<int> > C(n, tmp);

    for (int i = 0; i < n; i++) {
        for (int k = 0; k < n; k++) {
            for (int j = 0; j < n; j++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

これは私が速度をテストする方法です:

time boostImplementation.out > boostResult.txt
diff boostResult.txt correctResult.txt

time simpleImplementation.out > simpleResult.txt
diff simpleResult.txt correctResult.txt

両方のプログラムは、2つの2000 x 2000マトリックスを含むハードコードされたテキストファイルを読み取ります。両方のプログラムは、これらのフラグでコンパイルされました:

g++ -std=c++98 -Wall -O3 -g $(PROBLEM).cpp -o $(PROBLEM).out -pedantic

私の実装では15秒、ブーストでは4分 -実装!

編集:でコンパイルした後

g++ -std=c++98 -Wall -pedantic -O3 -D NDEBUG -DBOOST_UBLAS_NDEBUG library-boost.cpp -o library-boost.out

Ikj-アルゴリズムでは28.19秒、Boostでは60.99秒 。そのため、Boostは依然としてかなり低速です。

ブーストが実装よりもはるかに遅いのはなぜですか?

46
Martin Thoma

TJDが指摘したように、uBLASバージョンのパフォーマンスの低下は、後者のデバッグ機能によって部分的に説明できます。

デバッグをオンにしたuBLASバージョンの所要時間は次のとおりです。

real    0m19.966s
user    0m19.809s
sys     0m0.112s

デバッグをオフにしたu_BLASバージョンの所要時間(-DNDEBUG -DBOOST_UBLAS_NDEBUGコンパイラフラグが追加されました):

real    0m7.061s
user    0m6.936s
sys     0m0.096s

したがって、デバッグをオフにすると、uBLASバージョンはほぼ3倍高速になります。

残りのパフォーマンスの違いは、 BLAS FAQ 「なぜuBLASが(atlas-)BLASよりもずっと遅いのか」の次のセクションを引用することで説明できます。

Ublasの重要な設計目標は、可能な限り一般的であることです。

この一般性にはほとんどの場合、コストが伴います。特に、prod関数テンプレートは、スパース行列や三角行列など、さまざまなタイプの行列を処理できます。幸いなことに、uBLASは、特に axpy_prod およびblock_prodのような密行列の乗算に最適化された代替手段を提供します。さまざまな方法を比較した結果は次のとおりです。

ijkalgorithm   prod   axpy_prod  block_prod
   1.335       7.061    1.330       1.278

ご覧のとおり、axpy_prodblock_prodの両方が実装よりもやや高速です。 I/Oなしで計算時間のみを測定し、不必要なコピーを削除し、block_prod(64を使用)のブロックサイズを慎重に選択すると、違いがさらに大きくなります。

BLAS FAQ および 効果的なuBlasおよび一般的なコード最適化 も参照してください。

47
vitaut

あなたのコンパイラは十分に最適化されていないと思います。 uBLASコードはテンプレートを多用し、テンプレートは最適化を多用する必要があります。私はあなたのコードをMS VC 7.1コンパイラを1000x1000マトリックスのリリースモードで実行しました。

_10.064_ s for uBLAS

ベクトルの_7.851_ s

違いはまだありますが、決して圧倒的ではありません。 uBLASのコアコンセプトは遅延評価であるため、prod(A, B)は必要な場合にのみ結果を評価します。 prod(A, B)(10,100)はすぐに実行されます。実際には1つの要素のみが計算されるためです。そのようなものとして実際にあります 最適化できる全行列乗算専用のアルゴリズムはありません (下記参照)。しかし、あなたは図書館を少し助けて、宣言することができます

_matrix<int, column_major> B;
_

実行時間を_4.426_ sに短縮します。この宣言により、行列を乗算するときにメモリへのアクセスがよりシーケンシャルになり、キャッシュの使用が最適化されます。

追伸uBLASのドキュメントを最後まで読んだら、実際にwhole行列を一度に乗算する専用の関数があることに気付くはずです。 2つの関数-_axpy_prod_および_opb_prod_。そう

_opb_prod(A, B, C, true);
_

最適化されていないrow_major B行列でも_8.091_秒で実行され、ベクトルアルゴリズムと同等です

P.P.S.さらに多くの最適化があります:

_C = block_prod<matrix<int>, 1024>(A, B);
_

bがcolumn_またはrow_ majorであるかどうかに関係なく、_4.4_ sで実行されます。 「関数block_prodは、大密度行列用に設計されています」という説明を考慮してください。特定のタスクに特定のツールを選択してください!

13
panda-34

私は小さなウェブサイトを作成しました BLASを使用したマトリックス-マトリックス製品実験 。マトリックスマトリックス製品の新しい実装をuBLASに統合することです。ブーストライブラリが既にある場合、追加の4つのファイルのみで構成されます。だから、それはほとんど自己完結型です。

他の人が異なるマシンで簡単なベンチマークを実行できるかどうかに興味があります。

2
Michael Lehn