このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
はじめまして、藤原と申します。 私はフィックスターズのインターンに2週間参加し、2018年4月21日に弊社で開催された第72回オープンCAE勉強会@関東(流体など)【大崎】に成果発表をしてきました。 インターン後の今もアルバイトとして、プロジェクトに継続して参加しています。
この記事では特にプレゼンでは紹介しにくいソースコードに重点をおいて、発表内容の紹介をしたいと思います。
弊社では2017年12月に、OpenCAEシンポジウム2017@名古屋 講演会で、『OpenFOAMのスレッド並列化に向けて基礎検討』(富岡稔、伊藤優希、丸石崇史、吉藤尚生)についての講演を行いました。 この講演の内容を要約すると、
従来のOpenFOAMの疎行列の格納形式「lduMatrix」では、並列化が不可能である。 そのため、OpenFOAMのスレッド並列を実現するためには、少なくとも格納形式を並列化できるものに変更しなければならない。 そこで、弊社ではlduMatrixの代わりに、「CSR形式」を使用することで実際にスレッド並列化するための見通しを立てることに成功した。 しかし、CSR形式を用いてもDIC前処理 など、一部の処理ではデータ依存性により並列化することが難しく、Cuthill-Mckee法やMulticoloring法などを用いて工夫する必要がある。
という内容でした。 このブログではCuthill-Mckee法を実装し、DIC前処理を実際にスレッド並列で動作することを確認したので、そのアルゴリズムと実装方法についてまとめさせていただきます。
先述の発表にあるとおり、DIC前処理ではデータの依存性があるので、行毎に並列すると、ある一つのデータに関して計算中に書き込みと読み出しが同時に実行され、結果が不安定になる可能性があります。 計算結果が不安定にならないようにするために、以下の二種類の方法があります。
Cuthill-Mckee法(後述)とMulticoloring法の特徴を以下の表にまとめます。
アルゴリズム名 | 並列性 | バンド幅(キャッシュヒット率) | 収束性 |
---|---|---|---|
Multicoloring法 | Cuthill-Mckee法に比べて高い | Cuthill-Mckee法に比べて広い | 並列前より悪い |
Cuthill-Mckee法 | Multicoloring法に比べて低い | Multicoloring法に比べて狭い | 並列前と同じ |
今回はバンド幅が狭い点(キャッシュヒット率が高い)点と収束性が変わらない点に注目し、Cuthill-Mckee法を実装してみました。
もともと、Cuthill-Mckee法は並列化するためのアルゴリズムではなく、行列のfill-inやバンド幅を減らすためのアルゴリズムです。 Cuthill-Mckee法のアルゴリズムは、おおまかに以下の4ステップで構成されます。
このアルゴリズムだけでは、同じレベル内に隣接ノード(依存しあっている関係)が存在し、まだ並列化できません。 今回は、「レベルに新しくノードを追加する時に、すでに同じレベル内で隣接しているようなノードがある場合は追加しない」という処理を追加し、隣接ノードを排除しました。 詳細は中島研吾先生のスライドを参照してください。
いきなりOpenFOAM上に実装するのは苦なので、検証用コードにCuthill-Mckee法の関数を実装してみました。
i
は行 j
は列。i
行目 j
列目に値があれば、それは、i
-> j
に辺が伸びていると捉えると読みやすいと思います。
以下に行列を並び替えた結果を示しています。Multicoloring法に比べ、Cuthill-Mckee法はバンド幅が狭くなっている事がわかります。
Cuthill-Mckee法は並列化前と収束性は変わっておらず、Multicoloring法に比べ良くなっていることがわかります。
DICPreconditionerのコンストラクタでCuthill-Mckee法のレベルを計算し、DICPreconditioner::preconditionで求められたレベルを元に並列計算を行う。
という実装にしました。
もともとOpenFOAMに実装されていたpreconditionの前進代入に関しては、以下のようになっており、
for (label face=0; face<nFaces; face++)
{
wAPtr[uPtr[face]] -= rDPtr[uPtr[face]]*upperPtr[face]*wAPtr[lPtr[face]];
}
弊社でCSR形式に書き換えることで以下のようなコードになりました。
for (label i=0; i<n; i++)
{
for (label index = csr.offset[i]; index < csr.offset[i+1]; index++)
{
const auto j = csr.column[index];
if(j < i)
{
wAPtr[i] -= rDPtr[i] * csr.data[index] * wAPtr[j];
}
}
}
ここでwAPtr[i]
とwAPtr[j]
が相互依存し、並列化が出来なかったため、Cuthill-Mckee法を導入した結果が、以下のソースコードになります。
for (auto level = decltype(maxLevel)(0); level < (maxLevel); level++)
{
const auto rowStart = levelOffset[level];
const auto rowEnd = levelOffset[level + 1];
#pragma omp for
for (auto rowIndex = rowStart; rowIndex < rowEnd; rowIndex++)
{
const auto i = row[rowIndex];
const auto colStart = csr.offset[i];
const auto colEnd = csr.offset[i + 1];
for (auto colIndex = colStart; colIndex < colEnd; colIndex++)
{
const auto j = csr.column[colIndex];
if (j < i)
{
wAPtr[i] -= rDPtr[i] * csr.data[colIndex] * wAPtr[j];
}
}
}
}
row[]
には、レベル順にソートされた行番号が入っており、levelOffset[]
はレベルが変わるrow
のindexを表しています。
たとえば、
行番号 | レベル |
---|---|
0 | 2 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 2 |
であれば、 row
は [1(レベル1),0(レベル2),2(レベル2),4(レベル2),3(レベル3)] になり、 levelOffset
は [0,1,4,5] が格納されます。 ループを回す都合上、配列の終わりを表す 5
は入れておいたほうが楽なので入っています。
rowStart
とrowEnd
は同じレベル内のrow
のindexを表し、同じレベルであればどのような順番で計算してもi
とj
の間は依存関係がないため、
wAPtr[i] -= rDPtr[i] * csr.data[colIndex] * wAPtr[j];
のコードも、レベル内で並列計算しても安全に動作する。 という流れになっています。
結果、Cuthill-Mckee法を用いることでデータの依存関係があるDIC前処理を、安全に並列計算ができるようになりました。
まだOpenFOAMにはDIC前処理しかスレッド並列化を実装しておらず、またプロファイラも実装していないため、従来使用されてきたフラットMPIとの実行時間の比較が難しい状況になっています。 今後はDIC前処理以外の部分にもスレッド並列を適用し、ソルバ全体のスレッド並列化を試みつつ、プロファイラを実装し、個々の処理の実行時間の比較をしていきたいと考えています。
また、効果測定後にスレッド並列化したOpenFOAMのコードも公開する予定です。
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....