2021年10月21日木曜日

コレスキー分解のマルチスレッド化 Intel OneTBBの導入

 修正コレスキー分解を普通にマルチスレッド化すると意外なほど性能がでませんでした。

恐らくキャッシングの問題だと思われます。この問題に対して、Lapackの実装は、キャッシュ階層を考慮した設計になっています。

また、EigenのLinearSolver郡も試しましたが、Dense行列ではLLTならマルチスレッド化されて速いです。(LDLTは、未だのようです。)しかしSparse行列を食わせるとシングルスレッドのままで、性能が出ませんでした。メモリも多々食います。

ここでのジレンマは、小行列にするとキャッシュ効果は出るけれども、マルチスレッド化時の性能は出にくくなる、というもので、奥が深いです。可能ならば、Lapackを持ってくるのが正解でしょう。


記述が再帰ですので、綺麗にスレッドに分解できません。そこで、スレッドよりも粒度が細かいタスクを導入するためにIntelOneTBBを導入することにしました。

本当は、OpenMPの omp taskがVisual Studioで使えればよいのですが、未だ対応していないので苦肉の策です。(こちらで計測の通り、OpenMPの方が性能が高いようです。)

Intel OneTBBの導入方法

1)こちらからダウンロード展開

2)CMAKEでソリューション・プロジェクトファイル郡を生成

3)タスクの導入用には、tbbプロジェクトだけで十分

4)モジュール定義が”Visual”で、何故か区切れてしまっているので、コンパイルエラーになってしまいます。defファイルの場所を指定してやるとエラーなく動作しました。また、リンカーのコマンドラインの追加部分にも残骸が残っているので削除します。

5)debug/release用に各々DLLファイルをコンパイル生成


oneapi::tbbのタスクの記述例

if ( nThis >= NSIZEH ){//シリアル実行の方が速い場合はマルチスレッド化しない
        oneapi::tbb::task_group tg;

        tg.run([thisStruct, aTri, nThis, aUnder, diagonal, work, nLeft2, iBlock, jBlock, numberBlocks,level]() {
            CholeskyTAK(thisStruct, aTri, nThis, aUnder, diagonal, work, nLeft2, iBlock, jBlock, numberBlocks, level+1);
         });
        tg.run([thisStruct, aTri, nThis, aUnder ,  diagonal, work, nLeft , nLeft2, iBlock, nb, jBlock, numberBlocks,level]() {
            CholeskyTAK(thisStruct, aTri, nThis, aUnder + number_entries(nb), diagonal, work, nLeft - nLeft2,
                iBlock + nb, jBlock, numberBlocks, level+1);
        });

        tg.wait();
}else ..

上の記述を適用した後のCPU Usageです。Windows10のタスクモニタ(左下)では、60%程度安定で100%になる様子は観測できませんが、 Visual Studioのモニタ(右下)の方が粒度が細かいようで、頻繁に変化する様子が観測され100%になることもあることが分かります。つまり、空いているCPUリソースを遊びがないようにタスクとして働かせていると考られます。



0 件のコメント:

コメントを投稿