狠狠撸
Submit Search
短距離ハイブリッド並列分子動力学コードの設計思想と説明のようなもの ?並列編?
?
2 likes
?
5,162 views
Hiroshi Watanabe
Follow
短距离古典惭顿コード、主に通信まわりの设计で苦労したところ、悩んだところなど。
Read less
Read more
1 of 22
Download now
Downloaded 12 times
More Related Content
短距離ハイブリッド並列分子動力学コードの設計思想と説明のようなもの ?並列編?
1.
1/22 短距離ハイブリッド並列分子動力学コードの ? 設計思想と説明のようなもの?並列編? ? 東大物性研
? 渡辺宙志 2014年8月5日
2.
2/22 概要 本資料の目的 ?並列プログラム特有の「設計の難しさ」を共有したい ? ?っていうか単に「MPIの気持ち悪さ」を共有したい ? 設計思想 ?クラスが肥大化しすぎないようにしたい
? ?不必要なクラスを作り過ぎないようにしたい ? ?なるべくややこしいこと(通信の隠蔽とか)をしない ? 開発の歴史 まず?at-?‐MPI版を作成(Ver. ?1) ? その後、ハイブリッド並列版をスクラッチから作成 ?(Ver. ?2) ? Ver. ?1からVer. ?2で設計思想が変化 ?
3.
3/22 コードの概観 h6p://mdacp.sourceforge.net/ ファイルの置き場所 言語:C++ ? ライセンス: ?修正BSD
? ファイル数:50ファイル ?(*.ccと*.hがほぼ半数ずつ) ? ファイル行数: ?5000 ?lines ?(ぎりぎり読める程度?) ? 計算の概要 ? ?短距離古典分子動力学法 ?(カットオフ付きLJポテンシャル) ? ?相互作用、カットオフ距離は全粒子で固定 ? ?MPI+OpenMPによるハイブリッド並列化 ? プロセス/スレッドの両方で領域分割(pseudo-?‐?at-?‐MPI) ? ?アルゴリズムの解説 ? Prog. ?Theor. ?Phys. ?126 ?203-?‐235 ?(2011) ?arXiv:1012.2677 Comput. ?Phys. ?Commun. ?184 ?2775-?‐2784 ?(2013) ?arXiv:1210.3450
4.
4/22 MPIラッパークラス ?(1/2) とりあえずMPIのラッパークラスは作って置きたくなる ? Communicatorクラス
?(communicator.cc/.h) ? 静的メソッドのみ含む、事実上の名前空間 ? void ? Communicator::SendInteger(int ?&number, ?int ?dest_rank){ ? ? ?MPI_Send(&number, ?1, ?MPI_INT, ?dest_rank, ?0, ?MPI_COMM_WORLD); ? } 例:MPI_Sendのラッパー ? 例:std::vectorをやりとりするためのラッパー ? void ? Communicator::SendRecvIntegerVector( ? ? ? ? ?std::vector<int> ?&send_bu?er, ?int ?send_number, ?int ?dest_rank, ? ? ? ? ?std::vector<int> ?&recv_bu?er, ?int ?recv_number, ?int ?src_rank); ? ラッパークラスの役割: ? 型の明示、std::vectorの扱い、コミュニケータの隠蔽
5.
5/22 MPIラッパークラス ?(2/2) MPI_InitとMPI_Finalizeの隠蔽もすぐに思いつく ? ?main関数とライフタイムを共有する適当なクラス(ここではMDManager)を用意する
? ?そのコンストラクタでMPI_Initを、デストラクタでMPI_Finalizeを呼び出す ? int ? main(int ?argc, ?char ?**argv) ?{ ? ? ?MDManager ?mdm(argc, ?argv); ? ? ?if ?(mdm.IsValid()) ?{ ? ? ? ? ?ProjectManager::GetInstance().ExecuteProject(&mdm); ? ? ?} ?else ?{ ? ? ? ? ?mout ?<< ?"Program ?is ?aborted." ?<< ?std::endl; ? ? ?} ? } ← ?ここでMPI_Initが呼ばれている ? ← ?関数を抜けるときにMPI_Finalizeが呼ばれる ? main.cc ※ ?このコードは異常終了処理を考慮していない。正しく異常終了させる(=ユー ザの都合で異常終了する際にMPI_Finalizeが呼ばれることを保証する) ために は例外処理をするのが自然だが、手抜きにより実装していない。
6.
6/22 通信をどう設計するか? ?(1/3) とりあえず単純領域分割、?at-?‐MPIのみ考える ? すると、領域更新を担当するクラスを作るのが自然
? → ?ここではMDUnitと名付ける MDUnit MDUnit MDUnit MDUnit 実空間 分割された領域それぞれをMDUnitのインスタンスが管理 ? → 通信まわりをどう設計すべきか? ?
7.
7/22 通信をどう設計するか? ?(2/3) 案1: ?MDUnit同士が行う
? MDUnit MDUnit ?「隣の領域に誰がいるか」をMDUnitが自分で知っている必要がある ? ?「領域更新」という局所的な役割と、「全体把握」という大局的な ? 役割の同居がとても気持ち悪い ? → ??at-?‐MPI版では案1を採用 MDUnit MDUnit
8.
8/22 通信をどう設計するか? ?(3/3) 案2: ?MDUnitを管理するMDManagerクラスを作る
? MDUnit MDUnit MDUnit MDUnit MDManager ?MDUnitは自分が全体のどこに位置するか知らない ? ?通信は全てMDManagerを通して行う ? ?局所的役割と大局的役割の分離 ? → ハイブリッド版では案2を採用
9.
9/22 MPIの気持ち悪さ ?(1/3) ユーザ こういう動作を期待 こいつらだけが ? 並列動作する MDManager MDUnit MDUnit MDUnit MDUnit こいつが管理 すくなくともこういうイメージでMDManagerを作った
10.
10/22 MPIの気持ち悪さ ?(2/3) 実際にはこうなってる MDManager MDUnit MDManager
MDUnit MDManager MDUnit MDManager MDUnit こいつらみんな ? 並列動作する 並列動作するインスタンスを管理する「ただひとつの管理インス タンス」が存在しない ? →このようにクラスを分ける意味はあったのだろうか? ? ユーザ プロセス数に関係なく「ユーザから見てただひとつのインスタンスに ? 見える」オブジェクトがあれば、少なくとも設計はスッキリする? ※ ?ハイブリッド版では、一つのMDManager(プロセス)が複数のMDUnit(スレッド)を管理す るという意味もあるが???
11.
11/22 MPIの気持ち悪さ ?(3/3) MDManager 通信はMDManagerを通してのみ行いたい MDUnit MDUnit MDManager しかし実際には、ソースのどこからでもどこへでもMPI通信できる →
?MPIには本質的に「スコープ」が存在しない MDUnitに隣接する領域のランクを教えないことで ? 擬似的に「スコープ」を導入
12.
12/22 どの情報を誰が管理すべきか ?(1/2) MPIでは、ノードをまたぐ通信量をなるべく減らすように ? プロセスを配置する
? 0 1 4 5 2 3 6 7 8 9 11 12 10 11 13 14 ハイブリッドだとさらにややこしくなる。 ? → ?どの領域に誰がいるかの「地図」の管理が必要 1ノード4プロセス、4ノード計算のプロセス配置例
13.
13/22 どの情報を誰が管理すべきか ?(2/2) 案1: ?MPIInfoクラスを作って、そこで地図を管理
? ? ? ? ? ? ? ? ? ?通信するクラスがMPIInfoクラスのインスタンスを持つ 案2: ?MDManagerクラスが地図を直接管理してしまう ?at-?‐MPIコードの開発では案1を採用したが、 ? ハイブリッドコードの開発では案2を採用 ? ハイブリッドコードでは、MDManagerのコンストラクタ、デストラクタで MPI_Init/Finalizeを呼び出しており、MPI関連の情報を分離できていないこと、 及び分離することのメリットがあまりないことによる
14.
14/22 通信まわりの実装 ?(1/4) アルゴリズム ?相互作用距離よりも遠い粒子をペアリストに登録し、 しばらくリストを使いまわす(Bookkeeping法) ? ?端にある粒子の座標のみ通信(短距離相互作用)
? ?もらった粒子をさらに転送することで、斜め方向の通 信を省く(詳細は論文参照)。 考えるべきこと ?自分の粒子と他から借りている粒子をどうやって 区別するか ?送られてくる粒子情報が「どこから来た」か保存す べきか
15.
15/22 通信まわりの実装 ?(2/4) 自分の粒子と他から借りている粒子の区別 ? →
?配列を共有、粒子数を2つ用意した データ配列 自分が管理する粒子 送られて来た粒子 ParocleNumber ?(PN) TotalParocleNumber ?(TPN) ※この名前は良くなかった 実空間
16.
16/22 通信まわりの実装 ?(3/4) 送られてくる粒子情報が「どこから来た」か保存すべきか ? →「どこへ何を送るか」を覚えることで不要に 一番最初に送るときに「誰にどの粒子を送るか」をテーブルに保存。
? また、誰から何粒子もらうかも記憶しておく(MPI_Sendrecvの引数で必要だから)。 ? あとは同じ順番で送れば、同じ場所に同じ粒子の座標が送られてくるはず PN TPN 1. ?通信前にTPNをPNに合わせる 2. ?右から粒子をもらい、その数だけTPNをずらす PN TPN 3. ?以上の手続きを左、前後、上下で繰り返す。
17.
17/22 通信まわりの実装 ?(4/4) 自分の粒子と他から借りている粒子の区別 ? →二体関数以上の計算で必要 ポテンシャルエネルギーや圧力など、二体の関数について、そのまま計算する と、重複する分だけダブルカウントしてしまう。
? →「自分が管理する粒子」と「借りた粒子」の寄与は半分にする。 ? →「借りた粒子同士の寄与」は無視する ? 粒子番号のチェックだけでできる(原始的?) 三体以上の相互作用がある場合はどうするんだろう?
18.
18/22 main関数の引数を誰が受け取るか(1/4) ?MPI情報管理クラス: ?MPI_Initはargc, ?argvを要求
? ?パラメータクラス: ?ファイル名の取得にargvが必要 main関数の引数を要求するクラスが、少なくとも2つある 誰がどうやって受け取るべきか?
19.
19/22 main関数の引数を誰が受け取るか(2/4) 案1: ?argc, ?argvをMPI管理クラス(MPIInfo)とパラメータ管理 クラス(Parameterクラス)それぞれに渡し、それらのポイン タを管理クラスに渡す。 int
? main(int ?argc, ?char ?*argv[]){ ? ? ?MPIInfo ?minfo(argc, ?argv); ? ? ?Parameter ?param(argc, ?argv); ? ? ?MDUnit ?mdu(&minfo, ?¶m); ? ? ?//なにか処理 ? } ?at-?‐MPI版コードではこちらを採用 設計的にはこれがまっとうな気もする。 ?
20.
20/22 main関数の引数を誰が受け取るか(3/4) 案2: ?MDManagerにのargc, ?argvを渡し、コンストラクタで MPI_Initの処理やParameterのインスタンスを作る int
? main(int ?argc, ?char ?*argv[]){ ? ? ?MDManager ?mdm(argc, ?argv); ? } ? MDManager::MDManager(int ?&argc, ?char ?** ?&argv) ?{ ? ? ?MPI_Init(&argc, ?&argv); ? ? ?MPI_Comm_size(MPI_COMM_WORLD, ?&num_procs); ? ? ?MPI_Comm_rank(MPI_COMM_WORLD, ?&rank); ? ? ?std::string ?inpurile; ? ? ?if ?(argc ?> ?1) ?{ ? ? ? ? ?inpurile ?= ?argv[1]; ? ? ?} ?else ?{ ? ? ? ? ?mout ?<< ?"# ?Input ??le ?is ?not ?speci?ed. ?input.cfg ?is ?used." ?<< ?std::endl; ? ? ? ? ?inpurile ?= ?"input.cfg"; ? ? ?} ? ? ?param.LoadFromFile(inpurile.c_str()); ? ? } ? ハイブリッド版コードではこちらを採用
21.
21/22 main関数の引数を誰が受け取るか(4/4) Q. ?なぜ全てMDManagerに詰め込んだのですか? ? 分けたほうが設計がきれいだと思いますが? A.
?分けるご利益があまりないと考えたから その他雑多な感想 ? ?MPIInfo、Parameterクラスのインスタンスは、どちらもMDManagerのメンバに なっており、ライフタイムを共有している。MDManagerとライフタイムを共有する クラスのインスタンスを外で作って渡す、というのがどうにも気持ち悪かった。 ? ?プロセスの化身であるMDManagerが、自分のランクを自分で知らない、という のが気持ち悪い気がした。「プロセスの化身」は誰か?MDManagerか? MPIInfoか? ? ?main関数はなるべく簡素化したい(これは単に趣味)。
22.
22/22 まとめのようなもの 「相互作用が全て同一」という条件を最大限に利用した設計 ? ?粒子番号しかチェックしなくて済むのでシンプル。 ? ?粒子番号に意味を付与するのは拡張性に欠ける。
いずれ追加情報の管理が 必要になりそう。 ? → ?通信まわりを最適化してしまうと、相互作用の詳細に強く依存し、毎回作りな おしに近くなる? 通信をもう少し抽象的に扱いたい。 ? 通信の隠蔽を考慮していない ? ? ?原則としてMPI_Sendrecvしか使わないのでシンプル。デバッグが楽。 ? ? ?計算が比較的重いからできたこと。強スケーリングを追求すると破綻。 ? MPI、というかSPMDという設計思想に慣れるのに時間がかかった。 ? SPMDは「通信に関わる全体的な視点」と「送受信に関わるプロセスの局所的な 視点」の両方同時に要求する。「慣れろ」と言われればそれまでだが??? ? C++の言語仕様そのものに起因する問題で、設計にわりと苦しんだ ? っていうかC++はダメだと思う。GCのない言語で参照渡しの多用はいろいろ問題 がある。 ?
Download