Complete the 64-bit indexing of the partitioner
Close #818 (closed)
#733 (closed) / !509 (merged) で導入した idx_t 化には、未完了の箇所と、キャストで隠れていた誤りが残っていた。これを解消する。あわせて、64bit METIS での検証中に見つかった同種の問題も直す。
idx_t は狭めていない。typedef int idx_t や -Wno-error=incompatible-pointer-types は使っていない。
要判断: hecmw_varray_idx を partitioner 配下に置いた理由
新規の hecmw_varray_idx.[ch] を hecmw1/src/common/ ではなく hecmw1/tools/partitioner/ に置いた。判断を仰ぎたい点である。
hecmw_graph の格納型は idx_t に追従する必要がある。しかし autotools ビルドでは hecmw1/src/common/ に METIS のフラグが届かない。
| ディレクトリ | -DHECMW_PART_WITH_METIS |
-I$(METISINCDIR) |
|---|---|---|
hecmw1/tools/partitioner/ |
あり | あり |
hecmw1/src/solver/solver_direct_parallel/ |
なし | あり |
hecmw1/src/common/ |
なし | なし |
共通ライブラリ側に置くと、同ビルドで idx_t の幅が翻訳単位ごとに食い違う。本 MR が直しているのと同種の不具合になる。CMake は定義が全ターゲット共通なので問題は起きない。
共通ライブラリ側に置くべきなら、hecmw1/src/common/Makefile.am への METIS_CFLAGS 追加が要る(solver_direct_parallel/Makefile.am に前例あり)。libhecmw が METIS ヘッダに依存することになるため、方針を確認したい。
hecmw_varray_int の long long 化は解決にならない。32bit METIS では idx_t が int32_t のままで食い違いが残るためである。境界での詰め替えは大規模メッシュ対応を捨てるので採らない。
!509 (merged) が残していた問題
5件あった。うち3件は64bit METIS でクラッシュまたは誤った分割を招く。
| # | 箇所 | 内容 | 64bit METIS での影響 |
|---|---|---|---|
| 1 | 要素ベース分割経路 | elem_graph_index / item / belong_domain が int |
Bus error |
| 2 | hecmw_graph |
辺配列が int |
Segmentation fault |
| 3 | vwgt(頂点重み) |
int 配列を (idx_t *) でキャストして渡す |
誤った重みで分割 |
| 4 | 制約数 ncon |
int のアドレスを (idx_t *)&ncon で渡す |
4バイトを8バイトとして読む |
| 5 | METIS 4 | idx_t が未定義(METIS 4 は idxtype) |
— |
3と4はキャストで型検査が黙るため、コンパイラでは検出できない。1ea26714 は xadj / adjncy / part を広げた際、vwgt と ncon をキャストで辻褄合わせしたまま残していた。
5により、METIS 4 のビルドは !509 (merged) 以降できない状態だった。
| コミット | METIS 4 でのエラー数 |
|---|---|
| 1ea26714~1(!509 (merged) 直前) | 1(無関係の別要因) |
| 1ea26714(!509 (merged)) | 44 |
| bb0b7bed(現 master) | 44 |
hecmw_varray_idx.h に typedef idxtype idx_t; を追加して解消した。METIS 4 は64bit索引に対応しないので、同構成で32bitに留まるのは正しい。型が揃った結果、v4 分岐のキャストはすべて不要になり除去した。
idx_t と long long の使い分け
判断基準は「METIS に渡るか」である。
| 対象 | 型 | 理由 |
|---|---|---|
| METIS に渡る配列 | idx_t |
METIS の API が idx_t * を要求する |
| METIS に渡らない内部カウンタ | long long |
幅が idx_t 以上で、既存の書き方に合う |
idx_t が要る理由は配列ごとに違う。
| 配列 | 32bit超過 | 理由 |
|---|---|---|
elem_graph_index |
する | 値域とインタフェースの両方 |
elem_graph_item / belong_domain / vwgt |
しない | インタフェースのみ |
item / belong_domain / vwgt は値域だけなら int で足りる。しかし METIS は隣接配列も結果配列も重み配列も idx_t * で受ける。int のまま渡す方法は詰め替えしかない。
58b91e2f / 93d5a7d6 が elem_node_index のみを対象としたのは、elem_node_item が METIS に渡らないためである。
long long にしたのは3つ。create_elem_graph_link_list の戻り値と counter、create_elem_graph_compress の counter、create_elem_graph の n_graph である。いずれも METIS に渡らない。ノードベース経路の create_node_graph_compress と hecmw_part_edge_data.n_edge に合わせた。
ポインタのキャストは値を変換しない。 (idx_t)ncon なら問題ないが、(idx_t *)&ncon は4バイトのオブジェクトを8バイトとして読ませる。AddressSanitizer では stack-buffer-overflow として検出される。idx_t のコピーを作り、そのアドレスを渡す形に改めた。
型を広げても動作は変わらない。符号付き整数の拡大変換は値を保存するためである。
環境による差異
ある構成でビルドが通ることは、他構成について何も保証しない。
各コミットの hecmw_partition.c のエラー件数を4構成で数えた。
| コミット | METIS 4 | METIS 5 (32bit) | METIS 5 (64bit) | METIS 無し |
|---|---|---|---|---|
| bb0b7bed(修正前) | 44 | 0 | 9 | 8 |
| c0ae19bd | 0 | 0 | 9 | 8 |
| c0a0d0da | 0 | 0 | 7 | 6 |
| b8def435 | 0 | 0 | 1 | 0 |
| aa846bc4 | 0 | 0 | 0 | 0 |
既定構成だけが最初から0件である。GCC 13 以前ではこれらが警告に留まり、ビルドは通ってしまう。
64bit METIS での実測
METIS 5.1.0 を IDXTYPEWIDTH 64 でビルドして実行した。master も同じ METIS でビルドし(型不一致は警告に落とす)、同じ入力で比較した。
| ケース | 経路 | master | 本 MR |
|---|---|---|---|
nb_pmetis |
ノードベース(対応済み) | 正常 | 正常・結果一致 |
eb_pmetis / eb_kmetis |
要素ベース | Bus error | 正常 |
eb_rcb |
要素グラフのみ | 正常 | 正常・結果一致 |
nb_contact_agg |
hecmw_graph |
Segmentation fault | 正常 |
nb_contact_dist / _dist_k |
重み(ncon=2) |
正常だが結果が異なる | 正常 |
nb_contact_simple |
vwgt = NULL |
正常・結果一致 | 正常 |
本 MR が直す3経路が正確にクラッシュした。重みを渡す2ケースだけ結果が変わり、vwgt = NULL は一致した。
この構成で ctest -L mpi が 84/84 PASS である。
変更内容
| ファイル | 変更 |
|---|---|
hecmw_varray_idx.h / .c |
新規。hecmw_varray_int の idx_t 版。idx_t の typedef もここへ移し、METIS 4 用の定義を追加 |
hecmw_graph.h / .c |
辺配列を hecmw_varray_idx に。公開APIを idx_t に |
hecmw_partition.c |
要素ベース経路・重み・制約数を idx_t に。v5 / v4 双方のキャストを除去 |
matrix_repart.c |
METIS_NodeND に idx_t 配列を渡す |
CMakeLists.txt / Makefile.am |
新規ファイルを追加 |
頂点ID・分割数・degeneGraph の作業配列は int のままとした。値域が節点数・分割数に収まるためである。
matrix_repart.c は当初スコープ外としたが、"repart" の名のとおり分割に関わるため対応した。この経路のグラフは方程式数で決まり64bitの容量が要らない。よって idx_t の配列を呼び出しの前後で用意する形とした。パーティショナで詰め替えを採らなかったのは大規模メッシュ対応を捨てるためで、こちらは事情が異なる。
solver_direct_parallel はこれ以上触っていない。 同ディレクトリには型とは別の問題もあるが、METIS に直接関係する matrix_repart.c の修正に留めた。
残課題として直した軽微な2件
| コミット | 内容 |
|---|---|
| 81cf7381 | HECMW_graph_init が成功経路で is_ref を初期化していなかった。代入が return HECMW_SUCCESS; の後にあり、失敗経路でしか実行されなかった |
| 831130c0 | METIS_NodeND に用意済みの options が渡されていなかった。あわせて graph / perm / iperm を解放する |
コミット構成
| コミット | 内容 |
|---|---|
| c0ae19bd | hecmw_varray_idx の追加、idx_t typedef の移動と METIS 4 対応 |
| c0a0d0da | hecmw_graph の idx_t 化 |
| b8def435 | 要素ベース経路の idx_t 化 |
| aa846bc4 | 重み・制約数の idx_t 化、キャスト除去 |
| 8bad35fd | 並列直接法ソルバの METIS_NodeND 対応 |
| 81cf7381 | is_ref の初期化 |
| 831130c0 | options の受け渡しと解放 |
検証
ビルド
| 構成 | 修正前 | 本 MR |
|---|---|---|
| METIS 5・32bit(既定) | 成功 | 成功(診断件数の変化なし) |
| METIS 5・64bit | ビルド不可 | error 0 / warning 0 |
| METIS 4 | ビルド不可 | error 0 |
| METIS 無し | error 8 で失敗 | error 0 |
分割結果(既定構成、修正前後を cmp 比較)
8ケースの全16ファイルがバイト一致。nb_pmetis / eb_pmetis / eb_kmetis / eb_rcb / nb_contact_agg / nb_contact_dist / nb_contact_simple / nb_contact_dist_k である。
ctest
| 構成 | ラベル | 結果 |
|---|---|---|
| METIS 5・32bit | serial | 78/78 PASS(修正前と同一) |
| METIS 5・32bit | hybrid | 77/78 PASS(修正前と同一。1件は当方環境固有) |
| METIS 5・64bit | mpi | 84/84 PASS |
| METIS 無し | serial | 78/78 PASS |
32bit と64bit で一部ケースの分割結果が異なる。これは本 MR の影響ではない。METIS が発見的手法であることと、REALTYPEWIDTH が 32→64 に変わることによる。同一構成内での修正前後比較と mpi 全件 PASS で妥当性を確認した。
補足
- 大規模メッシュでの実測は今後の課題である。 本 MR で用いたのは小規模メッシュで、#733 (closed) が対象とする3億メッシュ級は確認していない
- CI への64bit METIS 構成の追加も今後の課題である。 1件加えれば、この種の未完了を早期に検出できる
- テストは追加していない。
tests/test.shは分割制御をMETHOD=KMETISの NODE-BASED で生成するため、要素ベース経路とCONTACT=AGGREGATE経路は ctest では実行されない。追加の要否は判断を仰ぎたい hecmw_varray_idxは17関数のうち7関数しか使っていない。hecmw_varray_intの対応版として揃えた。絞る方針であれば対応する- autotools(
setup.sh/make)でのビルドは未確認。Makefile.amの追記は既存エントリに倣っている - ドキュメントの変更は不要。入力仕様・キーワード・出力形式に変更がない