2.1 ループ変換およびスレッド数を動的変更する自動チューニング
名古屋大学情報基盤センター

片桐孝洋

2.1.1 はじめに
近年の計算機はマルチコア化が進み、メモリやキャッシュの構造や、Graphics Processing
Unit (GPU) を有する／有しないなど、計算機アーキテクチャが多様化している。そのため、数値計算ソフトウェアが高性能を発揮するためには性能チューニングが重要となる。しかし、計算機環境に合わせたコード最適化はハードウェアの専門知識が必要であり、手間と時間を必要とする。さらに、特定の計算機環境に特化したチューニングを行ったプログラ
ムは、他の計算機環境では性能が低下する可能性がある。そのため、計算機環境に応じ、再びチューニングが必要となるのが一般的である。
近年の計算機アーキテクチャのトレンドを考慮すると、ノード内のコア数が増加している。そのため、並列化対象となるループは、計算機ハードウェア上の並列性を満たすループ長が必要となる。もし並列化対象となるループ長が短い場合は、より長いループ長を持
つループを並列化対象にする必要がある。しかし一般的にループ長は、問題サイズ、Message
Processing Interface (MPI)、および、OpenMP で実行時に指定するスレッド数に影響し、静的に最適な並列化対象のループを見つけられないことがある。
一方、OpenMP 実行を考えると、実行時に指定するスレッド数が、対象となるループごとに最適なスレッド数ではないことがあり、対象となるループごとにスレッド数を動的に変更させると高速化できる可能性がある。
そこで本報告では、（1）OpenMP の対象ループを変更する自動チューニング方式；（2）OpenMPのスレッド数を実行時に変更する自動チューニング方式；の有効性を評価することを目的にした。
2.1.2 自動チューニング記述言語 ppOpen-AT
ppOpen-AT は、次世代の科学技術アプリケーションの開発・実行環境 ppOpen-HPC の自動チューニング機構として開発された自動チューニング言語である。並列数値計算ライブラ
リの開発効率を向上させることを目的に設計された FIBER 方式による自動チューニング機
能の付加を支援する自動チューニング言語（ディレクティブ）である。
ここでは、ppOpen-AT に実装可能な自動チューニング方式の検討と機能の有効性の評価を行う。
2.1.3 ループ変換の自動チューニング
2 重ループ以上の多重ループを OpenMP でスレッド並列化する場合において、並列化の対象ループを変更するループ変換を ppOpen-AT の拡張機能として考える。
ppOpen-AT では、!oat$ region start 〜 !oat$ region end で囲むプログラムを対象ループとする。拡張機能では、OpenMP のディレクティブの移動を伴うコード変換を行う。具
体的には、

!oat$ install Exchange(対象ループ番号, 対象ループ番号,…) region start〜!oat$ install Exchange(対象ループ番号, 対象ループ番号,…) region end
で囲むことで、交換するループを指定する。ただし対象ループには、OpenMP の並列化指定
が 1 つのみ記述されていると仮定する。対象ループ番号は、最外ループから数えて何番目のループを OpenMP の並列化対象とするかである。対象ループ番号をコンマで区切り指定
することで、複数のチューニング候補を生成可能である．
評価対象のコードは、プラズマ乱流解析コード GKV (p2) のサブルーチン exb̲realspcalの 4 重ループ（p3）である。このループを ppOpen-AT の collapse 構文により変換したループが p5 である。
名古屋大学情報基盤センター設置の Fujitsu PRIMEHPC FX100 での結果を p8 に示す。p8から、z 方向のループ長に応じて、本機能により生成されるループがオリジナルループより高速化されることがわかる。nz=1 のとき最大でオリジナルループの実行時間に対して 6.49
倍高速化される。ただし当該アプリケーションは nz=16 での実行が想定されているのでこの速度向上は参考値となるが、さらなる高速化のために nz 方向に MPI 並列数を増やさないといけない状況では、nz のサイズは上限で nz=1 まで小さくなることがありうる。また、並列数はそのままで、問題サイズを小さくする状況も考えられる。これらの状況においては、
本手法は効果を奏すると期待される。
2.1.4 実行時スレッド数変更の自動チューニング
ppOpen-AT による自動チューニングでは、性能パラメタを設定し、プログラムをチューニングする際に性能パラメタを変化させて性能を測定することで最適パラメタを本実行前に探索して決定する。その後、ユーザによるプログラムの実行時に、チューニング済みのパラメタを参照して設定し、最適な候補を実行時に指定して計算を行う。ここで検討する新機能は、性能パラメタとして OpenMP のスレッド数を指定し自動チューニングを行う機能である(p12)。OpenMP のスレッド数変更は、omp̲set̲num̲threads を使用した。
使用するプログラムは、有限差分法による地震波シミュレーションコード Seism3D を原型とする ppOpen-APPL/FDM である(p10、p11)。ppOpen-APPL/FDM のソースコード は
ppOpen-HPC のホームページから提供されている。OpenMP と MPI のハイブリッド実行ができる。ppOpen-APPL/FDM は ppOpen-AT によりコード選択の自動チューニング機能を実装しているが、ここでは、デフォルトの実装で性能評価を行う。
スレッド数の動的変更により、変更しない場合比較して FX100 の 8 プロセスで約 1.0014
倍、32 プロセスで約 1.012 倍の高速化が達成できた(p17)。
速度向上の理由を解析するため、当該ループを精密 PA 可視化により、メモリ・キャッシ
ュビジー率(p19)、および、キャッシュミス率(p20)の詳細性能プロファイルを行った。その結果、スレッド変更を行う場合は、メモリ・キャッシュビジー率の向上(L1 ビジー率、32%、

+5 ポイント)、キャッシュミス率（L1D ミス率、3.01%、-0.24 ポイント）と性能改善の理由が確認できた。
2.1.5 まとめ
本報告では、近年の計算機アーキテクチャのトレンドを考慮し、ノード内のコア数が増加していく場合の性能チューニング手法の検討を行った。並列化対象となるループは、計算機ハードウェア上の並列性を満たすループ長が必要とされる。もし並列化対象となるループ長が短い場合は、より長いループ長を持つループを並列化対象にする必要がある。一方、OpenMP での実行を考えると、実行時に指定するスレッド数が対象となるループごとに最適なスレッド数ではなく、対象となるループごとにスレッド数を動的に変更させると高速化できる可能性がある。
そこで本報告では、（1）OpenMP 対象のループを変更する自動チューニング方式；（2）OpenMPのスレッド数を対象ループごとに実行時に変更する自動チューニング方式；の有効性の検討を行った。その結果、双方の機能ともに速度向上に寄与することを確認した。
ポスト京コンピュータでは、計算ノードが 48 コア+4 アシスタントコア構成となり、ノード内のコア数が従来の計算機より増加する。そのため、本報告で示した機能は、現在より多くの局面において、より効果を奏すると期待される。
本機能を自動チューニング言語 ppOpen-AT へ実装することは今後の課題である。また本機能を、コンパイラによるコード最適化へ組み込むこと（ディレクティブの提供を含む）
は重要な課題であると著者は考えている。

ループ交換の自動チューニング

性能評価に使用するプログラム• GKV code (GyroKinetic Vlasov code) 1)
ジャイロ運動論的シミュレーションコード
核融合炉開発などに利用される、プラズマ乱流のシミュレーションを行うコード

1) Watanabe, T-H., and H. Sugama., “Velocity-space structures of distribution function in toroidal ion temperature gradient turbulence.,”
Nuclear Fusion 46.1, (2005):24.
2

評価対象とするループ• サブルーチンexb_realspcal (オリジナルループ)
do iv = 1, 2*nv!$OMP parallel do private(mx,my)do iz = -nz, nz-1
do mx = ist_xw, iend_xw
do my = 0, nyw
wkdf1_xw(my,mx,iz,iv) = cmplx(real(wkdf1_xw(my,mx,iz,iv),kind=DP) &* real(wkeyw_xw(my,mx,iz)-cs1 *vl(iv) &* wkbyw_xw(my,mx,iz),kind=DP )-real(wkdf2_xw(my,mx,iz,iv),kind=DP) &* real(wkexw_xw(my,mx,iz)-cs1*vl(iv)*wkbxw_xw(my,mx,iz),kind=DP ) &, aimag(wkdf1_xw(my,mx,iz,iv))*aimag(wkeyw_xw(my,mx,iz)-cs1*vl(iv) &* wkbyw_xw(my,mx,iz))-aimag(wkdf2_xw(my,mx,iz,iv)) &* aimag(wkexw_xw(my,mx,iz)-cs1*vl(iv)*wkbxw_xw(my,mx,iz)) &, kind=DP ) * cef
end doend doend do!$OMP end parallel doend do3

評価方法• それぞれのループの実行時間を計測して比較を行った• オリジナルループ (ループ1)• ppOpen-ATによるループcollapseで自動生成したループ(ループ2、3)• 提案手法によりppOpen-ATで自動生成されると想定されるコードを、手動で作成したループ (ループ4、5、6)• 実験の条件• 行列サイズnv=8 、ist_xw=0、iend_xw=127、nyw=64は固定する• 行列サイズnzを1から8に変化させる• OpenMP並列数を1から最大まで変える• ループを1000回繰り返し実行する
4

性能評価を行うループ(ループcollapse)
do iv = 1，2*nv!oat$ install LoopFusion region start!$OMP parallel do private(mx，my)do iz = (-nz)，nz-1
do mx = ist_xw，iend_xw
do my = 0，nyw! 省略
end doend doend do!$OMP end parallel do!oat$ install LoopFusion region end
end do• ppOpen-ATの機能によりループ
collapseを行い、2つのループを生成• 右のループはループの上半分のみ
ループ2 (ppOpen-AT の ループcollapse)

do iv = 1, 2*nv!$OMP parallel do private(mx,my,mx_my)
do iz = (-nz), nz-1
do mx_my = 1 , (iend_xw-ist_xw+1)*(nyw-0+1)
mx = mod((mx_my-1)/(nyw-0+1),(iend_xw-ist_xw+1)) + ist_xw
my = mod((mx_my-1),(nyw-0+1)) + 0

ループ3 (ppOpen-AT の ループcollapse)

do iv = 1, 2*nv!$OMP parallel do private(mx,my,iz)
do iz_mx_my = 1 , (nz-1-(-nz)+1)*(iend_xw-ist_xw+1)*(nyw-0+1)
iz = (iz_mx_my-1)/((iend_xw-ist_xw+1)*(nyw-0+1)) + (-nz)
mx = mod((iz_mx_my-1)/(nyw-0+1),(iend_xw-ist_xw+1)) + ist_xw
my = mod((iz_mx_my-1),(nyw-0+1)) + 0

5性能評価を行うループ!oat$ install Exchange(1，3，4)region start
do iv = 1，2*nv!$OMP parallel do private(mx，my)do iz = (-nz)，nz-1
do mx = ist_xw，iend_xw
do my = 0，nyw! 省略
end doend doend do!$OMP end parallel doend do!oat$ install Exchange(1，3，4)region end

提案するディレクティブの記述

ppOpen-ATにより自動生成されると想定されるコード
ループ4 (提案手法)!$OMP parallel do private(mx,my,iz)
do iv = 1, 2*nv
do iz = (-nz), nz-1
do mx = ist_xw, iend_xw
do my = 0, nyw

ループ5 (提案手法)

do iv = 1, 2*nv

do iz = (-nz), nz-1!$OMP parallel do private(my)do mx = ist_xw, iend_xw
do my = 0, nyw

ループ6 (提案手法)

do iv = 1, 2*nv

do iz = (-nz), nz-1
do mx = ist_xw, iend_xw!$OMP parallel dodo my = 0, nyw

6

実験環境

(プロセッサ、コア、スレッド数は1ノードあたり)

Fujitsu PRIMEHPC FX100 1)(以降，FX100)
計算ノード
プロセッサ名プロセッサ数 (コア数)
最大スレッド数
コンパイラ
コンパイルオプション

Fujitsu PRIMEHPC FX100
Fujitsu SPARC64 XIfx
1 (32)
32
frtpx: Fujitsu Fortran Driver
Version 2.0.0-Kfast -Qt -Cpp
-X9 -fs -fw -Kopenmp

1) 名古屋大学情報基盤センター設置

7実験結果 (FX100、1ノード32コア）
最速となるスレッド数での実行
ループ5(提案手法)

ループ4(提案手法)

8

実行時のスレッド数変更の自動チューニング使用するソフトウェア
ppOpen-APPL/FDM– 並列プログラム作成のためのライブラリ群
ppOpen-APPLのうち、差分法を対象にしたライブラリ– http://ppopenhpc.cc.u-tokyo.ac.jp/ppopenhpc/
downloads/ からppOpen-FDM var 0.3.1とppOpen-APPL/FDM-AT ver.1.0.0がダウンロード可能– 地震動のシミュレーションを行う Seism3D のプログラ
ムが原型
10

Seism3D1)• 地震動シミュレーションコード
• 差分法計算により陽的に地震波動の伝搬を計算するソフトウェア• 均質でない構造の地下を伝わる地震波を計算し、地面の揺れを求める• 地震と津波を連動して解くことができる
1) F. Mori, M. Matsumoto, T. Furumura,“Performance of FDM Simulation of Seismic Wave Propagation
using the ppOpen-APPL / FDM Library on the Intel Xeon Phi
Coprocessor”, Springer LNCS (2014).

www.nsc.riken.jp/target-application/Seism3D-jp.pdf

11本実験でのスレッド数変更方法
oat_max_threads = omp_get_max_threads()
で実行時のスレッドの値を保存しておく

ppOpen-ATにより自動生成
されると想定されるコード

変更前の処理外部の情報からNを決定する
omp_set_num_threads (N)

対象の処理
omp_set_num_threads (oat_max_threads)

変更後の処理12

予備実験• ppOpen-APPL/FDMのカーネル
update_stress に対してOpenMPスレッド数の変更を行い、効果を調査• 計算環境は名古屋大学情報基盤センター
設置のPRIMEHPC FX100（各ノード32コア）• 実験には8ノードを使用• プロセス数とスレッド数の組み合わせを変える• 2000ステップ計算する
13

実験環境
(プロセッサ、コア、スレッド数は1ノードあたり)

Fujitsu PRIMEHPC FX100 1)

Fujitsu PRIMEHPC FX100
プロセッサ名Fujitsu SPARC64 XIfx
プロセッサ数1(コア数)(32)
最大スレッド数
32
コンパイラ
mpifrtpx: Fujitsu Fortran Driver Version 2.0.0
コンパイルオプション -Kfast,openmp
計算ノード1) 名古屋大学情報基盤センター設置

14

評価方法
• スレッド数の変更をしない場合とする場合で全体実行時間を計測して比較を行った– スレッド変更なしは各プロセス数で最大のスレッド
数を使用– スレッド変更ありは各プロセス数で最大のスレッド数から、そのプロセス数で使用できるすべてのスレッドに変更し，それぞれの時間を計測• スレッド数変更の場合の環境変数– export OAT_EXEC=0– export FLIB_FASTOMP=FALSE– export FLIB_CTRL_BARRIER_ERR=FALSE

15

実験結果（FX100、8ノード）（動的変更スレッド数：２９）

180.00169.13168.89160.00140.00（動的変更スレッド数：４）

実行時間[s]120.0091.81
91.19

100.0080.00（動的変更スレッド数：３）
78.75
77.82

71.93
71.56

76.36
76.17

64

128

（スレッド数4）（スレッド数2）

256(ピュアMPI)

67.33
66.91

60.00
40.00
20.00
0.00

8

16

32

（スレッド数32） （スレッド数16） （スレッド数8）

プロセス数
スレッド変更なし（従来法）

スレッド変更あり（提案法）
16

実験結果（FX100、8ノード）（動的変更スレッド数：２９）
1.0150（動的変更スレッド数：４）
1.01201.0100

速度向上率

1.0050（動的変更スレッド数：３）

1.00141.00000.99750.99500.9937

0.9932

0.99480.99000.98500.980081632（スレッド数32） （スレッド数16） （スレッド数8）

64

128

（スレッド数4）（スレッド数2）

256(ピュアMPI)

プロセス数
17

詳細プロファイラ (精密PA)• FX100の精密PA可視化機能を使用• 8プロセスの場合の結果を比較• update_stressがプロファイル対象• スレッド変更ありの結果は、対象カーネルを全てのスレッド数に変えて試した結果，最速となった27スレッドで実行したケースを使用
18

メモリ・キャッシュビジー率• スレッド変更なし（従来法）：３２スレッド固定
0%

L1ビジー率10%

20%

L2ビジー率

30%

40%

50%

60%

メモリビジー率

70%

80%

90%

100%

90%

100%

27%
22%
49%

• スレッド変更あり（提案法）：当該カーネルのみ２７スレッド実行
0%

10%

20%

30%

40%

50%

60%

70%

80%

３２％（＋５ポイント）

5%
11%

19

キャッシュミス率• スレッド変更なし（従来法）：３２スレッド固定
0%

L1D ミス率 (/ロード・ ストア数)

L2 ミス率 (/ロード・ ストア数)

1%

6%

2%

3%

2.40%

4%

5%

7%

8%

9%

10%

3.25%

• スレッド変更あり（提案法）：当該カーネルのみ２７スレッド実行
0%

1%

2%

3%

2.23%

4%

5%

6%

7%

8%

9%

10%

３．０１％（−０．２４ポイント）20

2.2 非ブロッキング集団通信の通信隠蔽効果に関する調査
九州大学

南里

豪志

2.2.1 はじめにプロセス並列プログラムにおける通信の標準規格である Message Passing Interface(MPI) で定義されている非ブロッキング通信は、あるプロセス間の通信と、それらのプロ
セス上での計算を並行して進行させることにより、見かけ上、通信に要する時間の一部を計算時間で隠蔽することを可能とする。このうち、MPI-3.0 規格において採用された非ブロ
ッキング集団通信は、多くの並列プログラムで頻出する、複数のプロセス間でのデータ複製や集約のような定型通信である集団通信について、通信時間の隠ぺいを図るものである。
集団通信は、計算機の大規模化に伴って所要時間が増大するため、並列プログラムのスケーラビリティ向上の手段として、この非ブロッキング集団通信が期待されている。
非ブロッキング集団通信は、その集団通信に参加する各プロセスが計算を行っている間に、集団通信を完了させるための通信アルゴリズムを推進することで、通信時間を隠蔽するため、この、集団通信アルゴリズムの推進手法が、隠蔽効果に大きく影響する。現在、多くの MPI ライブラリで採用されている集団通信アルゴリズム推進手法のうち、特にプログレ
ススレッドを用いた推進手法は、プログラム中に MPI̲Test 関数のようなアルゴリズム推進のための MPI 関数を挿入しなくても高い通信隠蔽率が期待できる上、インターコネクトネットワークに特殊なハードウェアを必要としないという特徴があるため、特に注目されている。一方、この手法は、プロセス毎に集団通信アルゴリズム推進専用のスレッドに CPU
コアを割り当てる必要があるため、その分、計算性能が低下する。特に、スレッド並列とプロセス並列によるハイブリッド並列プログラムを利用する場合や、一つのノードの中で複数のプロセスを動作させる場合、この推進手法による非ブロッキング集団通信の効果は、計算性能低下による計算時間の増加と、通信隠蔽による通信時間の減少のトレードオフとなる。しかし、Intel MPI Benchmarks や OSU Micro-Benchmarks のような、一般に用いられているベンチマークプログラムにおける非ブロッキング集団通信の評価では、通信隠蔽
率のみを計測するため、この推進手法の実用性を十分に検証することが出来ない。
そこで本報告では、ハイブリッド並列計算と集団通信を並行して実行する簡単なベンチマ
ークプログラムを作成し、それを用いて、プログレススレッドによる非ブロッキング集団
通信の実用性を評価する。評価対象の MPI ライブラリとしては、PC クラスタ上でプログレ
ススレッドによる通信隠蔽を行う MVAPICH2-2.2 と Intel MPI 2017、さらに、Fujitsu
PRIMEHPC FX100 でプログレススレッド専用のアシスタントコアを使用する富士通社製 MPIを用いる。また、Mellanox 社の SHARP と呼ばれるオフロード機能についても、予備評価を行う。
2.2.2 非ブロッキング集団通信インタフェース
MPI-3.0 規格において採用された非ブロッキング集団通信インタフェースは、従来の一対一通信における非ブロッキング通信と同様、通信の開始と完了待ちをそれぞれ別の関数とすることで、通信完了を待つ間に、その通信と並行して処理可能な計算や通信の実行を可能とするものである。通信開始関数としては、MPI̲Iallreduce 関数や、MPI̲Ialltoall 関
数等、従来のブロッキング集団通信関数名に I を追加したものが用意されている。これらの関数は、完了を待つための情報を、MPI̲Request 型のリクエスト変数に格納する。この変数は、一対一の非ブロッキング通信と同じ MPI̲Wait 関数や MPI̲Waitall 関数等に指定することで、完了を待つことが出来る。これらの関数を用いて通信の開始と完了待ちを指示し、さらにその通信と関係のない処理を開始と完了待ちの間に挿入することで、その通信の時間の一部を他の処理で隠蔽することが期待できる。
現在、MPICH、MVAPICH2、Open MPI といった主要なオープンソースの MPI ライブラリの他、Intel MPI Library や富士通社製 MPI 等の非オープンソースの MPI ライブラリの多くで、この非ブロッキング集団通信インタフェースが提供されている。
2.2.3 非ブロッキング集団通信の実装技術
非ブロッキング集団通信による通信隠蔽の効果は、MPI ライブラリでの実装手段に大きく影響を受ける。特に、集団通信アルゴリズムを他の処理と並行して進行させるためのアルゴリズム推進の実装手段は、通信隠蔽効果への影響が大きい。この、アルゴリズム推進手
法は、集団通信アルゴリズムを構成する通信関数やメモリコピー、計算等の処理を、集団通信アルゴリズムで定義された依存関係に従って順に発行する。
現在、MPI ライブラリで採用されているアルゴリズム推進手法としては、主に以下の三通りが挙げられる。


MPI 関数呼び出し毎の推進

インターコネクトのオフロード機能による推進

プログレススレッドによる推進

このうち、MPI 関数呼び出し毎の推進とは、全ての MPI 関数内で、非ブロッキング集団通信を推進させるための推進ルーチンを実行するものである。この推進ルーチンは、その時点で進行中の全ての非ブロッキング集団通信について、アルゴリズムの依存関係に基づき、実行可能な命令を発行する。この推進手法を利用する場合、プログラマは、非ブロッキン
グ集団通信の開始関数と完了待ち関数の間に、例えば MPI̲Test 関数のように、プログラムの意味を変えない MPI 関数をプログラム中に挿入することで、完了待ちの前にアルゴリズ
ムを進行させておくことが出来る。この推進手法による性能向上の効果は、通信隠蔽による通信時間の削減と、推進ルーチンのためだけに呼び出す MPI 関数のオーバヘッドのトレードオフとなり、十分な効果が得られるか否かは、プログラムの構造やプログラマの能力に依存する。
一方、インターコネクトのオフロード機能による推進は、インターコネクトの Network
Interface Card (NIC) やスイッチ等に集団通信のアルゴリズムを進行させるオフロード機
能が用意されている場合に、非ブロッキング集団通信関数の内部でその機能を呼び出すものである。例えば Mellanox 社のインターコネクト技術である InfiniBand は、集団通信ア
ルゴリズムを NIC で処理する CORE-Direct 機能や、スイッチで処理する SHARP 機能を備えている。また、これらの機能を非ブロッキング集団通信の内部で呼び出すよう実装されている MPI ライブラリとしては、MVAPICH2 や Mellanox 社の HPC-X 、等がある。基本的に、オフロード機能を有するインターコネクトが利用できる場合、効果的に通信を隠蔽することが出来る。ただし、この機能は、NIC やスイッチのハードウェア上の制約により、使用できる集団通信関数やメッセージサイズ等が制限されている場合が多い。
これらに対して プログレススレッドによる推進は、MPI プログラムを進行させるスレッドとは別に、非ブロッキング集団通信アルゴリズムを進行させるためだけのスレッドを生成する。この推進手法は、使用するインターコネクトがオフロード機能を有しない場合や、プログラム中に適切に MPI̲Test 関数等を挿入することが困難な場合でも、集団通信と他の処理を同時に進行させることが出来るため、容易に通信を隠蔽する手段として注目されている。そのため、代表的なオープンソースの MPI ライブラリである Open MPI、MPICH、
MVAPICH2 のいずれも、プログレススレッドによる非ブロッキング集団通信の推進を選択可能となっている。このうち Open MPI では、Hoefler らが開発した非ブロッキング集団通信
ライブラリ LibNBC をコンポーネントとして追加することにより、この推進手法を実装している。この推進手法は、メインスレッドが、ハンドルキューと呼ぶ待ち行列を介して、集団通信アルゴリズムを構成する処理をタスク単位でプログレススレッドに渡すことにより、アルゴリズムを推進させている。一方、MPICH および MVAPICH2 は、同様の待ち行列を持つ推進手法を独自に開発し、実装している。
2.2.4 アシスタントコア機能
非ブロッキング集団通信の使用にあたって利用者が選択すべき事項のうち、性能に与える影響が大きいものとしては、前節までで紹介したアルゴリズム推進手法の他に、プログ
レススレッドへの CPU コアの割り当て方法が挙げられる。Fujitsu PRIMEHPC FX100 では、
アシスタントコアと呼ばれる CPU コアをプログレススレッドに割り当てることにより、計算用の CPU コアを全て計算に使用することが出来る。この機能は、FX100 向けの富士通 MPI
ライブラリで、mpiexec コマンドに以下のオプションを追加することにより、利用できる。
--mca opal_progress_thread_mode モード番号
ここでモード番号とは、プログレススレッドの動作モードを指定するもので、以下から選択する。
mode 1： 指定した区間のみプログレススレッドが動作する。区間内では MPI 関数を呼び出すことが出来ない。

mode 2： 指定した区間のみプログレススレッドが動作する。区間内で MPI 関数を呼び出すことが出来る。

mode 3： 非ブロッキング集団通信が呼ばれると自動的にプログレススレッドが動作する。

mode 1 および mode 2 の場合、MPI̲Ialltoall と MPI̲Wait に挟まれた計算部分を、以下のようにプログレススレッドの動作区間として指定することで、アシスタントコアを利用した通信隠蔽を図る。
MPI_Ialltoall(M);
FJMPI_Progress_start();do_comp();
FJMPI_Progress_stop();
MPI_Wait();
一方、アシスタントコアを持たない一般の PC クラスタなどの環境でプログレススレッドによるアルゴリズム推進を行う場合、Spare Core もしくは Fully Subscribed と呼ばれる方法で、CPU コアにプログレススレッドを割り当てる。
このうち Spare Core とは、プログレススレッドに一つの CPU コアを占有させるもので、高い通信隠蔽効果を期待できる。しかし、計算に割り当てる CPU コアが 1 プロセス当たり
1 コアずつ削減されるため、特に MPI のみによる並列プログラムや、ハイブリッド並列でプロセスあたりのスレッド数が少ない場合、計算性能の低下による影響が無視できなくなると予想される。
一方 Fully Subscribed は、計算用のスレッドに全ての CPU コアを割り当てておき、プロ
グレススレッドにはどれかの計算用スレッドと CPU コアを共有させるものである。これは、計算スレッドの空き時間に集団通信アルゴリズムを進行させることを期待するもので、
Spare Core と比べると、計算性能の低下による影響は少ないと期待できる。しかし、スレ
ッド切り替えのオーバヘッドや、進行状況を確認する頻度の低下により、通信隠蔽効果は低下すると予想される。
なお、Intel MPI Library には、プログレススレッドに割り当てる CPU コアの番号を固定する機能が用意されている。これにより、ノード内のプロセス数が増えても、プログレス
スレッドに割り当てられる CPU コア数が増加せず、計算性能の低下を軽減する効果が期待できる。
2.2.5 非ブロッキング集団通信性能評価用ベンチマークプログラム
非ブロッキング集団通信を対象としたベンチマークプログラムとして一般的に用いられているのは、オハイオ州立大学で開発されている OSU Micro Benchmarks や、Intel 社で開発されている Intel MPI Benchmarks 等に含まれるプログラムである。これらのプログラム
は、通信を隠蔽できた比率や、隠蔽できなかった通信オーバヘッドの計測を主な目的としている。そのため、通信と並行して実行する計算プログラムでは、予め計測していた通信
時間を用いて、通信が行われている間に計算が終了しないよう、計算量を制御している。
この手法は、通信隠蔽率の評価には適しているものの、以下の点から、この手法だけで実プログラムにおける非ブロッキング集団通信の効果を見積もることは困難である。


通信時間の実測値に応じて計測毎に計算量を自動調節するため、プログレススレッド

による計算性能低下の影響を評価できない。


計算部分がスレッド並列化されていないため、プログレススレッドの CPU コアへの割り当て方法による性能の変化を評価できない。

非ブロッキング集団通信を使わなかった場合の性能が計測されていないため、使用の有無によるプログラムの性能の変動を比較できない。

そこで本稿では、以下に示す簡単なプログラムにより、ハイブリッド並列プログラムにおける非ブロッキング集団通信の効果を計測する。
nn = N / procs;
MPI_Ialltoall(M);#pragma omp parallel for private(j,k)
for (i = 0; i < nn; i++)for (k = 0; k < N; k++)for (j = 0; j < N; j++)c[i*N+j] += a[i*N+k] * b[k*N+j];
MPI_Wait();
このプログラムでは、通信量 M と計算量 N を実行時に指定する。これにより、非ブロッキ
ング集団通信使用の有無による並列プログラムの性能への影響の見積もりが可能となる。
2.2.6 非ブロッキング集団通信による通信隠蔽効果の計測結果
2.2.6.1

アシスタントコアによる通信隠蔽効果

Fujitsu PRIMEHPC FX100 のアシスタントコアを用いた通信隠蔽効果は、添付のスライド
１に示すとおりである。今回試した MPI̲Iallreduce、MPI̲Ialltoall とも、ほとんどの場
合で、通信隠蔽の効果を確認できた。例外は、ノード内プロセス数を増やした場合のMPI̲Iallreduce、およびメッセージサイズが小さい場合の MPI̲Ialltoall であった。前者
は、集約の計算量、後者は、メッセージ毎に発生するオーバヘッドが相対的に増えたこと
により、アシスタントコアでの所要時間が計算時間を上回り、通信を隠蔽できなくなった、と考えられる。
2.2.6.2

スイッチへのオフロード機能による通信隠蔽効果（参考）参 考 の た め 、 Mellanox 社 に お け る ス イ ッ チ へ の オ フ ロ ー ド 機 能 SHARP に よ る
MPI̲Iallreduce の通信隠蔽効果を計測した。結果は添付のスライド２に示すとおりである。
今回試した実験のうち、本報告で作成した計測プログラムでは、メッセージサイズが小さ
く、ノード内プロセス数が少ない場合に、通信隠蔽効果が確認できた。一方、PETSc の PIPE
CG アルゴリズムでは、ノード内プロセス数が多い場合に、SHARP による高速化が見られる、という、矛盾した結果となっている。そのため、このオフロード機能による通信隠蔽については、より詳細な解析が必要である。

2.2.7 むすび
本報告では、非ブロッキング集団通信による通信隠蔽効果を調査した。従来の非ブロッキング集団通信ベンチマークプログラムでは、通信隠蔽に伴う計算時間の変動を考慮した評価が行えなかったため、計算量を固定したハイブリッド並列ループによる通信隠蔽ベン
チマークプログラムを作成した。
計測の結果、非ブロッキング集団通信をプログレススレッドで推進する場合、Fujitsu
PRIMEHPC FX100 のアシスタントコア機能が有効に働き、高い通信隠蔽効果が得られることを確認した。一方、非ブロッキング集団通信の推進をスイッチにオフロードする Mellanox
社の SHARP では、通信隠蔽の効果が正しく得られているかどうか、現時点では判断できず、
より詳細な解析が必要であることが分かった。

1

非ブロッキング集団通信の
通信隠蔽効果に関する調査
SS研 メニーコアWG
南里 豪志（九州大学）

2

研究の目的
• 非ブロッキング集団通信による通信隠蔽について、以下の技術による効果を検証• 検証１） Fujitsu PRIMEHPC FX100のアシスタントコア• 検証２） Mellanox社 SHARPによるオフローディング

3

背景： 非ブロッキング集団通信
Time• 集団通信の所要時間• プロセス数に応じて増通信• プログラム全体の並列化効果低減

計算• 非ブロッキング集団通信への期待：

通信隠蔽による並列化効果向上

Processes

プロセス数の増加に伴う並列プログラムの実行時間の変化• でも、多くのプログラムではアルゴリ

ズムの変更が必要• 通信と並行して行える計算の抽出

for(){
A();• 非ブロッキング集団通信が使い物になるか否か、予め知りたいTimeA
Alltoall

MPI_Ialltoall();B();
MPI_Wait();

B

C();

C

}

非ブロッキング集団通信による通信隠蔽

4

非ブロッキング集団通信のアルゴリズムをどうやって計算と並行して進めるか？
MPI関数呼び出しごとに推進

MPI_Ialltoall()Isend_task()
Irecv_task()

MPI_Test()

Irecv_task()
Wait_task()

MPI_Wait()

Isend_task()
Wait_task()
Wait_task()

プログレススレッド

MPI_Ialltoall()Isend_task()
Irecv_task()
Irecv_task()
Wait_task()

MPI_Wait()

Isend_task()
Wait_task()
Wait_task()

ネットワークデバイスにオフロード

MPI_Ialltoall()
MPI_Wait()

5

アルゴリズム推進手段の比較
通信隠蔽効果

使いやすさ

環境依存性

全体性能

MediumBad

Good

Medium

オフロード

High

Good

Bad

???

プログレススレッド

High

Good

Good

???

MPI関数毎に推進検証１） アシスタントコアによるプログレススレッドの効果
検証２） スイッチへのオフロードの効果6

プログレススレッドの利用手段：
コアにどのように割り当てるか？
• Spare Core• プログレススレッド用に1コアを留保TimeCore 0Core 1

通信
Core 2

Core 3〇 通信隠蔽率高
× 計算スレッド減に伴う計算時間増• Fully Subscribed• 計算スレッドとコア共有

計算

Time

Core 0Core 1

Spare Core

Core 2

Core 3〇 全コアで計算

検証１）• Assistant Core
• 計算機が提供する専用コアを使用

× スレッド切り替え
オーバヘッドTimeCore 0Fully SubscribedCore 1

Core 2

Core 3〇 全コアで計算× 計算機依存Assist
Core

〇 通信
隠蔽率高

Assistant Core7検証１） 実験• 計算環境• FX100• 名古屋大学情報基盤センター• Fujitsu SPARC64 XIfx 32コア, Tofu2• CX400• 九州大学情報基盤研究開発センター• Intel Xeon (Sandy Bridge) 16コア, InfiniBand FDR• 実施時期• 2017年 7月• 評価対象• 集団通信： Alltoall, Allreduce• 一対一通信： Ring (片方向のリング状隣接通信)8

FX100のアシスタントコア• 実行時オプション• モード番号：--mca opal_progress_thread_mode モード番号• 1: 指定区間（MPI呼び出し無し）• ２： 指定区間（MPI呼び出し有り）• ３： 自動区間• 区間指定• モード番号 1, 2のみ

ts = MPI_Wtime();
MPI_Ialltoall(M);
FJMPI_Progress_start();do_comp(N);FJMPI_Progress_stop();
MPI_Wait();Tovlp = MPI_Wtime() - ts;

9

CX400のプログレススレッド• Intel MPI• 環境変数I_MPI_ASYNC_PROGRESS=1• 追加オプション： プログレススレッドを割り当てる CPUを固定
I_MPI_ASYNC_PROGRESS_PIN=CPU番号• MVAPICH2• 環境変数
MV2_SMP_USE_CMA=0
MV2_ENABLE_AFFINITY=0
MV2_ASYNC_PROGRESS=1

10

非ブロッキング集団通信の効果計測：
OSU Benchmark• オーバラップ率重視• 通信時間に応じて計算量を調整• 実験毎に計算量が変動⇒ 実装手段の相互評価が困難• 計算まで含めた全体的な効果の評価が困難

ts = MPI_Wtime();
MPI_Ialltoall();
MPI_Wait();
Tcomm = MPI_Wtime() - ts;

通信時間

ts = MPI_Wtime();
MPI_Ialltoall();tcs = MPI_Wtime();
通信時間を
while (MPI_Wtime() - tcs < Tcomm)超えるまで計算
dummy_comp();
Tcomp = MPI_Wtime() - tcs; 計算時間
MPI_Wait();
Tall = MPI_Wtime() - ts; 全体時間
overlap = (Tall - Tcomp) / Tcomm;11

今回使用したプログラム
Overlap Test

get_param(&M, &N);

• 通信量、計算量はパラメータ指定• 同じパラメータによる全体時間で実装手段の優劣を評価• ブロッキング集団通信の時間も計測• 実装手法相互の比較• 全体的な効果の評価

ts = MPI_Wtime();
MPI_Alltoall(M);Tcomm = MPI_Wtime() - ts;

ブロッキング
集団通信時間

ts = MPI_Wtime();
MPI_Ialltoall(M); MPI_Wait(); 非ブロッキング
Tnbcomm = MPI_Wtime() - ts;

集団通信時間

ts = MPI_Wtime();do_comp(N);Tcomp = MPI_Wtime() - ts;
ts = MPI_Wtime();
MPI_Alltoall(M);do_comp(N);Tblock = MPI_Wtime() - ts;
ts = MPI_Wtime();
MPI_Ialltoall(M);do_comp(N);MPI_Wait();Tovlp = MPI_Wtime() - ts;

計算時間
ブロッキング
全体時間

オーバラップ
全体時間

12

計算コード
• NxN密行列積• プロセス並列：• 行列 A, Cをプロセス分割• スレッド並列：• 最外ループを並列化• スケジュール： static or dynamic
do_comp(N)nn = N / procs#pragma omp parallel for private(j, k) schedule (...)
for (i = 0; i < nn; i++)for (k = 0; k < N; k++)for (j = 0; j < N; j++)c[i*N+j] += a[i*N+k] * b[k*N+j];13

FX100: Alltoall
48nodes, 48procs, 1MB, 1536x1536

comp+comm: 通信時間と計算時間の和
comp: 計算時間
comm: ブロッキング集団通信時間
nbcomm: 非ブロッキング集団通信時間
blocking: ブロッキング集団通信+計算
normal: 非ブロッキング集団通信+計算（プログレススレッド無し）
mode1〜mode3: 非ブロッキング集団通信+計算（プログレススレッドあり）

Static- 通信をほぼ全部隠蔽- Dynamicスケジューリングの方が若干遅い

Dynamic

14

FX100: Allreduce
48nodes, 48procs, 1MB, 768x768

static sched- 通信をある程度隠蔽- Dynamicスケジューリングの方が若干遅い

dynamic sched

15

FX100: Ring
48nodes, 48procs, 1MB, 384x384

static sched- 通信をほぼ全部隠蔽- Dynamicスケジューリングの方が若干遅い

dynamic sched16

スケジューリングポリシーによる行列積計算時間の違い17

Analysis of Static

負荷バランス

18

Analysis of Dynamic

負荷バランス- L2キャッシュミスによる性能低下

19

FX100:
Alltoallwithvarying
nodes
1MB, static
12nodes 12procs, 768x768

24nodes 24procs, 1536x1536

48nodes 48procs , 1536x1536

96nodes 96procs , 1536x1536- ノード数によらず、通信をほぼ全部隠蔽

20

FX100:
Allreducewithvarying
nodes
1MB, static
12nodes 12procs, 384x384

24nodes 24procs , 384x384

48nodes 48procs, 768x768

96nodes 96procs , 768x768- 96ノードで、blockingより mode3が低速- 12〜48ノードでは、nbcommの方が commより高速

21

FX100:
Alltoallwithvaryingprocs/node
1MB, static
48nodes 48procs, 1536x1536

48nodes 96procs, 1536x1536

48nodes 192procs, 3072x3072

48nodes 384procs, 3072x3072- ノード内プロセス数を上げても、通信を隠蔽- 集団通信時間はプロセス数に応じて増加

22

FX100: Allreduce with varying procs/node

1MB, static

48nodes 48procs, 768x768

48nodes 96procs , 768x768

48nodes 192procs , 768x768

48nodes 384procs , 768x768- ノード内プロセス数を上げると、プログレススレッドで性能悪化

23

FX100:
Small
messagesize
48nodes, 48procs, 128bytes, Static

Alltoall, 384x384

Allreduce 96x96- Alltoallは、性能悪化- Allreduceは、自動区間モードで性能向上

24

FX100:
Overlapping
multiplecollectives
48nodes, 48procs, 1MB, Static
Alltoall+Alltoall, 1600x1600

Allreduce+Allreduce, 960x960- 複数の集団通信も隠蔽可能

25

CX400 Intel: Alltoall
32nodes, 32procs, 1MB, 1024x1024
Static, Full

Static, Spare

Static / Dynamic: OpenMPのスケジューリング
Full / Spare: プログレススレッドのコアへの割り当て
Dynamic, Full

Dynamic, Spare- Spare コアの利用により計算時間は若干延びるが全体時間は Fullより短い

26

CX400 MVAPICH2: Alltoall
32nodes, 32procs, 1MB, 1024x1024
Static, Full

Dynamic, Full

Static, SpareDynamic, Spare- Intel MPIとほぼ同様

27

CX400 Intel: Allreduce
32nodes, 32procs, 1MB, 512x512
Static, Full

Dynamic, Full

Static, SpareDynamic, Spare- Alltoallとほぼ同様

28

CX400 MVAPICH2: Allreduce
32nodes, 32procs, 1MB, 512x512
Static, Full

Static, SpareDynamic, Full

Dynamic, Spare- プログレススレッドにより性能悪化29検証１） まとめ• 非ブロッキング集団通信の効果• プログレススレッド専用コアがある場合、通信隠蔽効果が期待できる• 効果が得られなかったケース：• メッセージサイズが小さい場合• ノード数やノード内プロセス数が多い場合の Allreduce• MVAPICH2の Allreduce• OpenMPのスケジューリングポリシー• 少なくとも今回の場合、全体性能としては staticの方が良い場合が多かった• 計算に依存する30

検証２） 集団通信のオフロードの効果• Mellanox社 SHARP• InfiniBandスイッチに「集約型」の集団通信をオフロード• 対象：
Allreduce, Reduce, Bcast, Barrier

MPI_Iallreduce()
MPI_Wait()• 入力データと出力データのサイズが同じ• 集約操作で実現可能• 利用法：

Mellanox社の HPC-X中の MPIライブラリで以下を指定-x HCOLL_ENABLE_SHARP=2-x HCOLL_ENABLE_NBC=1-x HCOLL_POLLING_LEVEL=1• MVAPICH2 2.3 Release Candidate 2 でも利用可能らしい

31

Aggregation Tree
• SHARPが Aggregation Nodeとプロセスで構成する

仮想的な木構造• Aggregation Node: スイッチや計算ノードに配置する仮想的なノード

Aggregation Trees
Aggregation Nodes
Processes

32

SHARPによる Allreduce• Aggregation Tree上で Aggregation Messageにより上方へ集約し、結果を下方に伝搬• Aggregation Message: 最大 256バイトのペイロードを持つ集約用メッセージ• 256バイトを超えるサイズ： 複数のメッセージに分割• 各 Aggregation Nodeで、集約演算の計算順序を保証

Aggregation Message

33

SHARPへの期待• 高い通信隠蔽率• Allreduce, Iallreduceの所要時間短縮• データ転送経路上で演算するためデータ転送量を削減• OSジッタ等、プロセス間の不均衡な処理による影響軽減• CPUにおける待ち時間が不要

34

通信隠蔽効果の計測手段• OSU Micro Benchmarks• Ohio State Universityが開発• 通信の隠蔽率を計測• Overlap Test
• 自作
• 隠蔽後の実行時間を計測• PETSc• Argonne National Laboratory が中心となって開発• 通信隠蔽アルゴリズムの効果を計測35検証２） 実験• 計算環境• スーパーコンピュータシステム ITO• 九州大学情報基盤研究開発センター• Intel Xeon Gold 6154 (3.0GHz, 18core) x 2 / node
• Mellanox InfiniBand EDR• Red Hat Linux Enterprise 7.3• gcc 4.8.5• 使用MPIライブラリ• Mellanox HPC-X 1.9.5, MVAPICH2 2.2, Open MPI 2.1.3• 実施時期• 2018年7月• 対象プログラム• OSU Micro Benchmark• Overlap Test （検証１）と同じ）• PETSc

36

PETSc• 偏微分方程式でモデル化されたアプリケーションの開発ツール• 通信を隠蔽した反復法アルゴリズムを提供
CG

PIPE CGdo {b = beta/betaold;ierr = VecAYPX(P,b,Z);/* p <- z + b* p */dpiold = dpi;
ierr = KSP_MatMult(ksp,Amat,P,W); /* w <- Ap*/
ierr = VecXDot(P,W,&dpi);/* dpi <- p'w*/
KSPCheckDot(ksp,dpi);
betaold = beta;a = beta/dpi;/* a = beta/p'w */ierr = VecAXPY(X,a,P);/* x <- x + ap*/
ierr = VecAXPY(R,-a,W);/* r <- r - aw*/
ierr = KSP_PCApply(ksp,R,Z);/* z <- Br*/
ierr = VecXDot(Z,R,&beta);/* beta <- r'*z */KSPCheckDot(ksp,beta);
dp = PetscSqrtReal(PetscAbsScalar(beta));ksp->rnorm = dp;
ierr = (*ksp->converged)(ksp,i+1,dp,&ksp->reason,ksp->cnvP);
if (ksp->reason) break;ierr = VecXDot(Z,R,&beta);/* beta <- z'*r */KSPCheckDot(ksp,beta);i++;} while (i<ksp->max_it);

do {VecNormBegin(R,NORM_2,&dp);
VecDotBegin(R,U,&gamma);
VecDotBegin(W,U,&delta);
PetscCommSplitReductionBegin(R);
KSP_PCApply(ksp,W,M);/* m <- Bw */KSP_MatMult(ksp,Amat,M,N);/* n <- Am */VecNormEnd(R,NORM_2,&dp);
VecDotEnd(R,U,&gamma);
VecDotEnd(W,U,&delta);
dp = PetscSqrtReal(PetscAbsScalar(gamma));ksp->rnorm = dp;(*ksp->converged)(ksp,i,dp,&ksp->reason,ksp->cnvP);
if (ksp->reason) break;beta = gamma / gammaold;alpha = gamma / (delta - beta / alpha * gamma);
VecAYPX(Z,beta,N);/* z <- n + beta * z */VecAYPX(Q,beta,M);/* q <- m + beta * q */VecAYPX(P,beta,U);/* p <- u + beta * p */VecAYPX(S,beta,W);/* s <- w + beta * s */VecAXPY(X, alpha,P); /* x <- x + alpha * p */VecAXPY(U,-alpha,Q); /* u <- u - alpha * q */VecAXPY(W,-alpha,Z); /* w <- w - alpha * z */VecAXPY(R,-alpha,S); /* r <- r - alpha * s */gammaold = gamma;i++;} while (i<ksp->max_it);

集約操作開始集団通信開始集約操作完了待ち37

実験で検証したいこと• 基本的な MPI_Allreduceの性能、

通信隠蔽率• OSU Micro Benchmarks の MPI_Iallgather向けコードを

MPI_Iallreduce向けに改変• 通信隠蔽による全体の実行時間への影響• Overlap Testプログラム• PETScの通信隠蔽アルゴリズムの効果• tutorialの ex3.c を CG, PIPE CGで実行• 前処理： Jacobi

38

MPI_Allreduce, Iallreduceの所要時間(OSU Micro Benchmark)128ノード 16バイト
Allreduce, Iallreduceとも
HPC-X with SHARPがほとんどの場合で最速
ノード内プロセス
数が多い場合、Allreduceの性能低下

39

MPI_Allreduce, Iallreduceの所要時間(OSU Micro Benchmark)128ノード 8192バイト

サイズが大きくなると、
SHARPの効果なし

40Overlap Test
128ノード 16バイト"No Ovlp": 通信と計算を逐次実行"Ovlp": 通信と計算を並行実行
ノード当たりプロセス数が少ない場合
HPC-X with SHARPで Ovlpによる
実行が最速

41

Overlap Test
128ノード 4096バイト
メッセージサイズが大きくなると
Ovlpと No Ovlpでほぼ同性能

42

PETSc with Jacobi前処理
128ノード 1536x1536

43

PETSc with Jacobi前処理
128ノード 3072x3072

44検証２） 現時点のまとめ• Allreduce, Iallreduceの素性能、および Overlap Testは、- メッセージサイズが小さく- ノード内プロセス数が「少ない」
SHARPで通信隠蔽時が高速• PETScの CG with Jacobi では、問題サイズが大きく、ノード内プロセス数が「多い」場合に
PIPE CG + SHARPが最速• 結果に矛盾が見られるため、通信隠蔽の効果について、より詳細な解析が必要

2.3 アプリケーションの性能予測について
理化学研究所

R-CCS 南一生

2.3.1 ルーフラインモデル
プロセッサ単体の実行性能を引き出すチューニングの研究に関連し、プログラムの限界
性能を予測する手法として、Williams らは Roofline Model(ルーフラインモデル)を提案した。この中で、ソースプログラムの情報から得られるアプリケーションの要求するoperational intensity(Flop/Byte)を用い、ハードウェアの理論メモリバンド幅および CPU
理論ピーク性能から、アプリケーションの限界性能が予測可能であることを示した。ルー
フラインモデルの概要を図 1 に示す。

•
•
•
•
•

図１

ルーフラインモデル

我々も、STREAM ベンチマークで測定された実効メモリバンド幅を理論メモリバンド幅の代わりに用いることで、ルーフラインモデルによる性能予測が、実アプリケーションの性能を良く予測できることを確認した。しかしルーフラインモデルは、プログラムの要求
operational intensity(Flop/Byte)の計算において、キャッシュに載っているデータについては考慮しておらず、キャッシュの効果を考慮しないモデルとなっている。実際、以下の図 2 のように性能予測結果と実測性能に乖離が生じることが観測されている。下図において赤枠内のデータは L2 キャッシュアクセスとなっており、ルーフラインモデルによる性能予測値であるピーク性能比 39%に対し実測値は、14.7%となっている。
do k=1,96
do n=1,16769
メモリアクセス
scl(n,k,l)=(&+cdiv(0,n,l,1)*vx(n,k,l) &
+cdiv(1,n,l,1)*vx(n+1 ,k,l) &+cdiv(2,n,l,1)*vx(n+131,k,l) &+cdiv(3,n,l,1)*vx(n+130,k,l) &+cdiv(4,n,l,1)*vx(n-1 ,k,l) &キャッシュ +cdiv(5,n,l,1)*vx(n-131,k,l) &+cdiv(6,n,l,1)*vx(n-130,k,l) &アクセス+cdiv(0,n,l,2)*vy(n,k,l) &:
+cdiv(0,n,l,3)*vz( n,k,l) &:)*factenddo
Fortranによるプログラム
enddo

メモリバンド幅のみを考慮した
ルーフラインモデル(S.
Williams et al. 2009)による性能

予測と実測値
性能予測値=

ハードウェアの実効B/F値
要求B/F値

図 2 性能予測結果と実測性能の乖離の例

2.3.2 「京」でのルーフラインモデルの検証
この検証では、配列サイズを変化させることにより、全ての配列がメモリに載った状態(on メモリ)、または L2 キャッシュに載った状態(onL2)となるテストプログラムを用意した。
このテスト要求バイト数を変えないように、演算数を増やし、要求 b/f 値を変化させた。
すべての変数が on メモリの場合のメモリ性能テストの結果を図 3 に示す。
0.8

予測性能 vs b/f値
実測性能 vs b/f値

0.7

ピーク 性能比

0.6
0.5
0.4
0.3
0.2
0.1
0
0

5

10
b/f値

図 3 実効メモリバンド幅を使用したルーフラインモデルの検証(on メモリ)
この結果から、変数がメモリ上にある場合にはルーフラインモデルによる予測値が実測値と良く一致していることが確認された。次に、L2 キャッシュ性能テストの結果を図 4 に示す。
1.0

予測性能 vs b/f値
実測性能 vs b/f値

0.8

0.6

0.4

0.2

0
0

5

10
b/f 値

図 4 実効 L2 キャッシュバンド幅を使用したルーフラインモデルの検証(onL2 キャッシュ)要求 b/f 値が 2.0 以上の L2 キャッシュのバンド幅の平均値から、L2 キャッシュの実効バンド幅を 159.2GB/s とした。実効バンド幅と理論ピーク性能から、L2 キャッシュの理論
バンド幅 256GB/s、理論 B/F 値 2.0 を用いれば、L2 キャッシュの実効 B/F 値は159.2/128=1.24 となる。要求 b/f 値が 1.24 よりも大きい場合に、予測性能と実測性能がほぼ一致していることが分かる。要求 b/f 値が 1.24 より小さい場合は、予測性能がピー
ク性能になっている。特別にチューニングされた DGEMM のような数学ライプラリでも実効性能としてピーク性能が得られることはなく、ここでも同様に要求 b/f 値が 1.24 よりも小さい場合は、実測性能はピーク性能まで届いていない。このような場合、演算器律速になっており、L2 キャッシュのバンド幅を使い切ることができないため、L2 キャッシュバ
ンド幅の測定値が低くなっている。これらの結果から、L2 キャッシュにおいても L2 キャ

ッシュのデータ供給能力に対し、演算器律速になっていない場合であれば、実効バンド幅を使用することにより、ルーフラインモデルが適用可能であることが明らかになった。
2.3.3 メモリとキャッシュ混合状態での性能限界値予測モデル
2.3.3.1 限界性能予測モデル
実効性能は、プログラムで実行される演算量を実行時間で除した値であるので、実行時間の予測ができれば限界性能の予測が可能となる。メモリ、L2 キャッシュ、演算に対して、
メモリデータ転送量、実効メモリバンド幅、L2 キャッシュデータ転送量、実効 L2 キャッシ
ュバンド幅、プログラム演算量、演算実効性能を、それぞれ DM、BM、DL2、BL2、NC、Peffとする。またメモリ律速時の実行時間を tM、L2 キャッシュ律速時の実行時間を tL2、演算器律速時の実行時間を tC とする。メモリ律速時はデータ量を実効メモリバンド幅で割ることにより tM が求められる。L2 キャッシュ律速時も同様である。これらのモデルを図 5 に示す。
メモリデータ転送量： DM

実効メモリバンド幅： BM

L2データ転送量： DL2

演算量： Nc
実効L2バンド幅： BL2

演算ピーク性能： Peff

メモリデータ移動時間： tM=DM/BM
L2データ移動時間： tL2=DL2/BL2

最小演算時間： tC=Nc/Peff

実行時間： t E =max { tM , tL2 , tC }
ピーク性能比： C p = N c/ (max { tM , tL 2 , tC } × Peffp eak )
図 5 メモリとキャッシュ混合状態での性能限界値予測モデル(1)tE =tM、tE =tL2、tE =tc、のそれぞれの場合の状態の概念図を図 6 に示す。

図 6 メモリとキャッシュ混合状態での性能限界値予測モデル(2)2.3.3.2 ルーフラインモデルとの関係
ルーフラインモデルを 2.3.3.1 に出現した変数を書き換えると図 7 のようになる。この結果と 図 5 を比較すると、従来のルーフラインモデルは、図５のモデルにおいて L2 キャ
ッシュの効果を考慮していないモデルであると言える。

性能(Flops)=Cp*Peff = min(F, BX)= min(Peff,BM*Nc/DM)
F:実効ピーク 性能: Peff
B： 実効メ モ リ バン ド 幅: BM= min(Peff,Nc/(DM/BM))

X ： 演算強度: f/b=Nc/DM

tM = D M / B M= min(Peff,NC/tM)

tC = N c/ P eff= min(NC/tC,NC/tM)= NC/max(tC,tM))

tE=max(tC,tM)
図 7 ルーフラインモデルの書き換え

2.3.3.3 「京」での限界性能予測モデルの検証
混合性能テストとして、図 8 に示したプログラムを用いた。このプログラムにおいて、
配列:c(i,j-1,k)、c(i,j,k)、 c(i,j+1,k)のうち１つはメモリからのロードとなるが、他の２つについてはキャッシュに載っている。配列 c の 1 次元目の宣言が、1 から 4000 であるので、配列 c の第 2 添え字に関しては、4000 × 8 = 32kB 離れており、ループ内に a と cの 2 つの配列アクセスが存在することを考えると、 c(i,j+1,k)は L1 キャッシュには載り切らず L2 キャッシュアクセスとなる。メモリ上の a(i,j,k)へのアクセス数を 2 とすると、メモリへの要求バイト数 3×8=24(B)、L2 キャッシュへの要求バイト数 2×8=16(B)、演算数
は 2 となるので、メモリへの要求 b/f 値は 12、L2 キャッシュへの要求 b/f 値は 8 となる。
3 変数のメモリへのアクセス、2 変数の L2 キャッシュへのアクセス、演算が 2 であるので、カーネルループ名を 3M-2L2-2F と呼ぶこととしこのコードをベースコードとした。このコードに対し、4 行目の代入文の右辺の式に、(右辺)*c(i,j+2,k)+c(i,j-2,k)のように項を追加することにより、L2 キャッシュのアクセスと演算数を増加させた。メモリアクセス数、
L2 キャッシュアクセス数、演算数の比を m:n:l とした場合のカーネルループを mM-nL2-lFと呼ぶことにした。
mM-nL2-kF

mnk

3M-2L2-2F
do k = 1,N3
do j = 1,N2
do i = 1,N1a(i,j,k) = c(i,j-1,k)+c(i,j,k)*c(i,j+1,k)1F 1L2 1F 1L2
enddo
1M
2M
enddo
enddo

tM=tL2

図 8 メモリ・L2 キャッシュ混合テスト
図 8 に示したようにベースコードのメモリへのアクセス数を固定し、L2 キャッシュへのアクセス数、演算数を変化させ測定を行なった。メモリ・L2 キャッシュ混合テストの結果を図 9 に示す。測定に用いたカーネルループ一覧を図 9 に示した。カーネルループ番号の増加と共に、L2 キャッシュアクセス量および演算量が増加している。実効演算性能:Peff
は、図 4 の b/f 値 0.5 で得られた実測性能(peak 比)0.88 を用い、Peff = PPEAK× 0.88で計算した。

1.6

t M < t L2
1.4

t M > t L2
1.2

時間(sec)

1.0
0.8
0.6
0.4
0.2
0
0
Table 5.3: メ モ リ ・ L2 キャッ シ ュ 混合ア ク セス 性能テ ス ト ケース
カ ーネル
カ ーネ ルカ ーネル
カ ーネル
ループ 番号 ループ 名
ループ 番号 ループ 名
1
3M -2L2-2F
15
3M -10L2-10F
2
3M -3L2-4F
16
3M -10L2-20F
3
3M -4L2-4F
17
3M -10L2-40F
4
3M -5L2-6F
18
3M -10L2-80F
5
3M -6L2-6F
19
3M -10L2-100F6
3M -6L2-12F
20
3M -12L2-12F
7
3M -6L2-24F
21
3M -12L2-24F
8
3M -6L2-48F
22
3M -12L2-48F
9
3M -6L2-78F
23
3M -12L2-60F
10
3M -8L2-8F
24
3M -12L2-120F
11
3M -8L2-16F
25
3M -14L2-28F
12
3M -8L2-32F
26
3M -14L2-56F
13
3M -8L2-64F
27
3M -14L2-84F
14
3M -8L2-128F
28
3M -14L2-140F

5

10

15

カーネルループ番号

20

25

30

tM
tL2
tC
実測時間

Fig. 5.8: メ モ リ ・ L2 キャッ シュ 混合ア ク セス 性能テ ス ト 結果

図 9 メモリ・L2 キャッシュ混合テストの結果<1>
図 9 から、tM>tL2 となっているカーネルループ 1-9 では、実測時間はほぼ tM に一致し、また tM <tL2 となっているカーネルループ 10-28 では、実測時間は、ループ 9、14、19、2428 を除き、ほぼ tL2 に一致していることがわかる。ループ 9、14、19、24、28 は、tL2<tcとなっており、実測時間は tc に近い。ただしカーネルループ 9 については実測時間が tcと少しずれている。これは、演算性能については Peff を PPEAK の 0.88 倍としているが、演算性能が、この値まで到達することは保証の限りではなく、多少誤差が生じる傾向にある。ここで、tM>tL2 から tM <tL2 に切り替わる点は、計算することができ、この境目はループ 9 とループ 10 の間となるが、実測値とも一致している。
次に、従来のルーフラインモデルと提案した予測モデルの比較を行う。図 10 は、ピーク性能比のルーフラインモデルによる予測値と実測値をプロットしたものである。図中の括
弧つきの数字は、カーネルループ番号である。図 10 左上は、メモリバンド幅律速の場合(tM>tL2)の場合であるが、予測値と実測値がよく一致していることが分かる。一方、図 10
左下は、カーネルループ 10-28 についてのものであるが、tM<tL2 の領域であるため、従来のルーフラインモデルによる予測値と実測値の乖離があることがわかる。図 10 右下は、
tM<tL2 の領域に新しい予測モデルを適用した結果であり、予測値と実測値がよく一致していることを示している。

メモリ・ L2キャッシュ混合テスト結果

1.0
(9)

0.9

予測性能
実測性能

0.8
(8)

ピーク性能比

0.7
0.6

(9)

Williamsらの(8)

ルーフラインモデル

0.5
0.4

(7)

0.3

(7)

tM>tL2

(6)

0.2
(6)

0.1

(4)(5)の領域(2)(3)(1)(4)(5)(2)(3)

0
0

(1)

5

提案モデル

10

要求b/f値

1.0(14)(28)(24)(19)(18)(27)
(13)

1.0

(14)(18)(28)(24)(19)
(13)

予測性能
実測性能

(23)

(14)(28)0.8(24)(19)(18)

(26)

(27)

(28)(24)0.8(14)(19)(18)

ピーク性能比ピーク性能比

(22)
(13)
(27)

(17)

0.6
(23)

(12)
(25)

(26)
(22)
(17)
(12)

0.4

(21)

(13)
(27)

0.6

(23)

0.4

(25)(21)(16)(11)の領域(26)(22)(17)(12)

(26)
(22)
(17)
(12)
(25)

0.2

(20)
(15)

(20)(15)(10)(21)(16)(11)(25)(21)(16)(11)

(10)

0
0

tM<tL2

(23)

(16)
(11)

0.2

予測性能
実測性能

0
0

10

(20)(15)(10)(20)(15) (10)要求b/f値

510

要求b/f値

図 10 メモリ・L2 キャッシュ混合テストの結果<2>
2.3.4 実アプリケーション性能予測と評価
実アプリケーションを例として、図 2 のプログラム((A)とする)を含む 4 つのプログラムに対し、本モデルによる性能予測と実測性能を基に、プログラムのチューニングを行った
結果について示す。今回の性能予測の目的は、限界性能を予測することであり、実際のア
プリケーションで必ずしも限界性能値を得られるわけではない。そこで今までの経験に基づきチューニングによる性能向上の目標は、実測性能が予測性能の 85%程度を達成することとした。性能予測と評価のサマリを図 11 に示す。

Table 5.6: 4 つのプ ロ グラ ムの性能予測結果

(A)
(B)
(C)
(D)

n M nL 2 n L 1s n L 1l
l
5 21
12
6 43
13
2
3
15 60
11
2
0
2 11
3
8
8
0 25

予測性能
tuning 前 ( バン ド 幅:GB/s)tuning 後 ( バン ド 幅:GB/s)
従来法
提案法
実測性能 メ モ リ L2cache 実測性能 メ モ リ L2cache(peak 比) (peak 比) (peak 比) バン ド 幅 バン ド 幅 (peak 比) バン ド 幅 バン ド 幅
0.387
0.236
0.147
23.0
95.7
0.197
29.7
121.8
0.208
0.208
0.129
29.6
55.8
0.193
43.7
70.7
0.045
0.045
0.038
45.6
35.8
‒
‒
‒
0.375
0.324
0.290
38.4
128.5
‒
‒
‒

図 11 実アプリケーション性能予測と評価

(A)は、L2律速の見積りとなる。
•

従来法の予測より提案法の予測値がL2アクセスを反映し低くなった．

•

最初の実測値は低かったがチューニングにより性能向上。予測値に近づいた。

(B)は、メモリ律速の見積もりとなる。
•

最初の実測値は低かったがチューニングにより性能向上。予測値に近づいた。

(C)は，メモリ律速の見積もりとなる。
•

実測値と予測値はかなり近い値となっている。

(D)は，L2律速の見積もりとなる。
•

従来法の予測より提案法の予測値がL2アクセスを反映し低くなった。

•

実測値と予測値はかなり近い値となっている。

2.3.5 実アプリケーション性能予測と評価
プログラムをチューニングする場合、限界性能を予測することは極めて重要である。ルーフラインモデルを拡張したモデルにより、キャッシュアクセスがある場合でも性能予測ができるようになり、チューニングの際の一つの指標として用いることが出来る。性能予測モデルを実際のプログラムに適用し、提案した性能予測モデルでプログラムの限界性能値を予測し、チューニング実施の判断基準にすることができることを示した。
以上

2.4 富⼠通C++コンパイラの最適化機能の改善について
東京⼤学物性研究所 物質設計評価施設
渡辺宙志
富⼠通株式会社 次世代テクニカルコンピューティング開発本部
千葉修⼀

2.4.1 はじめにHPC分野においては、FortranないしC/C++⾔語が広く使われている。Fortran、C⾔語、C++⾔語のいずれも活発に仕様が改定され、現在も発展し続けている⾔語ではあるが、数値計算向けの「普段使いの⾔語」としては、おそらくPythonやJuliaなどの⽅が広く使われていると思われる。特にPythonは、昨今の機械学習ブームの牽引役として広く使われているようである。今後、PythonやJuliaといった⾔語によるスパコン利⽤も増えていくと思われるし、それにとどまらずRustのような次世代⾔語も使われるようになって欲しいと期待もするのだが、スパコンを使うためにFortran/C/C++が必要となる現状は当⾯続くであろう。
さて、スパコンを使うからには、プログラムは⾼速に実⾏されて欲しい。そのためには優秀な最適化機能を備えたコンパイラがあることが望ましい。ところが、FortranやC⾔語はともかく、⾔語仕様が巨⼤でプログ
ラムの記述⾃由度が⾼いC++⾔語はコンパイラから⾒ると最適化が困難な⾔語である。スパコンユーザは、
普段のコード開発をx86系のPC上で、GCC/clangもしくはインテルコンパイラを⽤いて⾏っていることが多い。x86上におけるGCCやclangの最適化機能は⾼度であり、かなり抽象的なレベルで最適化を⾏う能⼒を持っている。また、インテルコンパイラはSIMDベクトル化や、⼤域最適化(Link Time Optimization, LTO)に優れている。スパコンユーザはそういった「優秀」なコンパイラを使い慣れているため、富⼠通C++コンパイラの挙動に⼾惑う場合がある。本WGでは、渡辺が2017年7⽉25⽇に富⼠通のC++コンパイラの問題点と改善の要望を挙げた。また、関連して2018年1⽉24⽇にマルチスレッド環境下におけるメモリ管理の問題について報告した。コンパイラについての問題については、富⼠通の千葉が2017年10⽉20⽇と2018年10⽉19⽇の⼆回にわたり、富⼠通のコンパイラ改善の現状と今後の⾒通しについて発表した。メモリ管理の問題については、2019年2⽉25⽇に富⼠通より報告があったが、こちらの内容については別の報告書にまとめ、本稿では主にコンパイラに対する要望と改善内容について述べる。なお、本WGにおいて渡辺は分⼦動⼒学法における⼒の計算のAVX2、AVX-512を⽤いたSIMDベクトル化⼿法とその性能向上についても報告を⾏ったが、本稿において詳細には触れない[1]。ポスト京アーキテクチャはSIMD幅が512ビットになることが発表されており、今後ますます性能におけるベクトル演算の重要性は増していくと思われる。コンパイラによる⾃動ベクトル化も重要なトピックと思われるが、本稿ではC++⾔語に特有の最適化にフォーカスする。

2.4.2 クラス変数の定義場所に依存する最適化処理
C++⾔語のようなクラスベースのオブジェクト指向⾔語においては、いくつかの変数や関数をまとめてクラスとして管理する。 渡辺が開発した分⼦動⼒学法コードMDACP[2]では、座標と運動量をまとめてclass Variables{
public:double C0;
double q[N][D],p[N][D];};という変数クラスを作って管理していた。Nが原⼦数、Dが次元を表し、座標q及び運動量pをそれぞれ多次元
配列として宣⾔してある。また、C0はカットオフに使われる定数である。このVariableクラスのインスタン
スを受け取り、⼒の計算をするコードを書いていたのだが、⼒の計算だけを抜き出したコードは最適化され、満⾜いく性能が出るにもかかわらず、それとほぼ等価と思われる本番コードは最適化されないことに悩んでいた。本件について調査した結果、コンパイラの最適化機能がクラス内のメンバ変数の定義順序に依存することがわかった。例えば、以下のようなトイコードを考えてみる。本コードでは変数C0は使われない。

void
calcforce(Variables *vars){
double (*q)[D] = vars->q;
double (*p)[D] = vars->p;for(int i=0;i<N-1;i++){const double qx = q[i][X];const double qy = q[i][Y];const double qz = q[i][Z];double px = p[i][X];double py = p[i][Y];double pz = p[i][Z];for(int j=i+1;j<N;j++){double dx = q[j][X] - qx;
double dy = q[j][Y] - qy;
double dz = q[j][Z] - qz;
double df = (dx*dx + dy*dy + dz*dz);
px += df*dx;
py += df*dy;
pz += df*dz;
p[j][X] -= df*dx;
p[j][Y] -= df*dy;
p[j][Z] -= df*dz;}
p[i][X] = px;
p[i][Y] = py;
p[i][Z] = pz;}}

これはVariableクラスのインスタンスを受け取り、距離から定まる適当な⼒を計算して運動量に⾜し込むコードである。 冒頭で座標や運動量のポインタを定義して代⼊しているが、これは(少なくとも京稼働当初の
富⼠通C++コンパイラでは)こうしないと最適化がかからなかったためである。 さて、このコードをそのま
まコンパイラに⾷わせるとソフトウェアパイプライニング(Software pipelining, SWP)がなされない。
「京」や「FX10」は、豊富なレジスタを活かして⼒任せにループをソフトウェアパイプライニングすることが前提のアーキテクチャとなっており、ソフトウェアパイプライニングが効かないと性能が出ないことが多い。しかし、ここでVariableクラスの宣⾔順序を⼊れ替えてみる。

class Variables{
public:double q[N][D],p[N][D];double C0; // 宣言を後ろにずらした};

すると先程の⼒計算のループにソフトウェアパイプライニングが適⽤され、実⾏性能が倍以上向上した。 今回のケースをユーザから⾒ると、「使われないクラスのメンバ変数の定義位置により、プログラムの性能が倍以上変わる」という奇妙な結果となる。 後に、restrict修飾されたポインタ間の最適化処理に問題があったのが原因と報告された。 このようにクラスとして保持されたデータのポインタを受け取る、という処理
はC++のコンテナでは頻出するイディオムであるため、 同様な理由で性能が出ないユーザコードが他にもあることが想定される。この件については今後改善を検討する旨が報告された。

2.4.3 STLコンテナの最適化について
C++⾔語においては、⽣の配列を使わずにstd::vectorといったコンテナを使うのが⼀般的である。 コン
テナに対するループ処理も、ループカウンタではなくイテレータを⽤いる。コンパイラとしてGCCやインテ
ルコンパイラを使っている場合、コンテナを使っても⽣の配列を使った場合に⽐べて特に性能は劣化しない。しかし、富⼠通C++コンパイラはSTLコンテナを使うとループの最適化がうまくできない場合がある。
先程と全く同様なコードを、std::vectorを使って書き直してみよう。

const int N = 20000;
struct pos{double x,y,z;};
void
calcforce(std::vector<pos> &q, std::vector<pos> &p){
for(int i=0;i<N-1;i++){const double qx = q[i].x;
const double qy = q[i].y;
const double qz = q[i].z;
double px = p[i].x;
double py = p[i].y;
double pz = p[i].z;
for(int j=i+1;j<N;j++){double dx = q[j].x - qx;
double dy = q[j].y - qy;
double dz = q[j].z - qz;
double df = (dx*dx + dy*dy + dz*dz);
px += df*dx;
py += df*dy;
pz += df*dz;
p[j].x -= df*dx;
p[j].y -= df*dy;
p[j].z -= df*dz;}
p[i].x = px;
p[i].y = py;
p[i].z = pz;}

このコードはソフトウェアパイプライニングされず、性能が出ない。 可変⻑配列であることが悪いのかと、固定⻑配列コンテナであるstd::arrayを使っても最適化されず、 引数に⽣配列を渡すと最適化が効き、妥当な性能が出る。なお、GCC、インテルコンパイラともにstd::vector、std::arrayを使っても⽣配列を使った場合と遜⾊ない性能がでることは付記しておきたい。
後に、STLの内部で使われているvolatile変数が最適化を阻害していることが報告された。こちらについても改善を検討中とのことである。

2.4.4 Ranged-base forについての最適化
std::vectorその他、要素を多数保持するコンテナクラスについて、その要素全てについてなんらかの処理をする、といったプログラムは頻出する。例えば、std::vector<int> vが保持する要素全てを2倍したい、という時、従来のfor⽂を使ったプログラムでは以下のように書けるであろう。

for (unsigned int i = 0; i < v.size(); i++) {v[i] *= 2;}

これは、C++11から導⼊されたranged-base forを⽤いて以下のように書ける。

for (auto &t : v) {t *= 2;}

さて、通常のforを使ったコードとranged-base forを使ったコードは等価なのであるから、同じように最適化処理がなされて欲しい。
以下のようなコードを書いてみよう。

const int N = 10000000;
double a[N];voidmyabs(void){for(auto &v: a){if(v < 0.0)v = -v;}}

単純に、グローバル配列aについて、その絶対値を取る関数である。このコードをコンパイルさせると、SIMD化もソフトウェアパイプライニングも適⽤されない。通常のforを使ったコードに書き換えてみる。

const int N = 10000000;
double a[N];void
myabs(void){for(int i=0;i<N;i++){if(a[i] < 0.0)a[i] = -a[i];}}こちらはSIMD化もソフトウェアパイプライニングも適⽤されており、アセンブリを⾒てもfneg,sや
fcmplted,sといったSIMD命令が発⾏されていることが確認できる。
後にこれはループの解釈、とくに終了判定がカウント型(不等式による判定)であるか、終端判定型(等式による判定)であるかの違いが最適化に影響していること、後に改善を検討することが報告された。

2.4.5 構造体に対する最適化
C++では、データのまとまりを構造体として扱うことが多い。 また、演算⼦オーバーロードを⽤いて、構造体やクラスをあたかも基本型として演算したりするなど、柔軟な記述ができる。 以下は、⼆つの整数の組を要素にもつベクトルを表現することを意図した構造体である。

struct IntPair{
int i,j;};
IntPair add(IntPair x, IntPair y){
IntPair z = {x.i + y.i, x.j + y.j};return z;}
void
func(int &x, int &y){
IntPair a = {1,2}, b = {3,4};
IntPair c = add(a,b);
x = c.i;y = c.j;}

IntPairは整数を⼆つまとめた構造体で、IntPair addはその演算⼦オーバーロードを模した関数である。
関数func内では、⼆つのペアの和を計算し、その要素を参照の形で返却している。 さて、この関数の実⾏結
果は常にx = 4, y = 6となるが、最近のコンパイラはそれを認識して、 無駄な構造体を作らず、値を即値で返してしまう。例えば以下はg++ -O3 -Sの実⾏結果である。

func(int&, int&):
movl$4, (%rdi)
movl$6, (%rsi)
ret4と6を即値で返していることがわかる。しかし、富⼠通コンパイラは関数funcについて以下のようなアセン
ブリを吐く。

func(int&, int&):add%sp,-240,%sp
mov
1,%xg3mov
2,%xg4stw%xg3,[%sp+2255]
mov
3,%xg5mov
4,%xg6
mov
3,%xg7
mov
4,%xg8
mov
1,%g1
mov
2,%g3
add
%g1,3,%g2add%g3,4,%g4
stw%xg4,[%sp+2259]
stw%xg5,[%sp+2263]
stw%xg6,[%sp+2267]
stw%xg7,[%sp+2247]
stw%xg8,[%sp+2251]
stw%g1,[%sp+2239]
stw%g3,[%sp+2243]
stw%g2,[%o0]
stw%g4,[%o1]
sub%sp,-240,%sp
retl

なお、わかりやすいように遅延スロットの場所を変えるなどしている。 このアセンブリを読み解くと、関数のインライン展開は⾏われたが、関数ローカルに(すなわちスタックに) 作られた構造体が、後に不要だとわ
かっても削除できなかったことが推定される。 ワークアラウンドとしては、add関数の引数を参照渡しに変えたり、構造体にコンストラクタを書くことで 最適化が最後まで通り、富⼠通コンパイラでも即値で返せるようになる。 しかし、それを知らないユーザから⾒ると、⾃分の開発環境では全く問題にならなかった場所に余計なコードが⼤量に⽣成されており、知らないうちに性能が劣化している、ということになる。この現象をコンパイルメッセージから読み解くのは難しく、性能劣化に気づいた後にアセンブリを詳細に読み解かなければ、何が起きているかを把握できないであろう。
これについては、引数で受けた構造体に関する最適化処理が終盤で⾏われているため、そのステージでは ソースコードの情報が⽋落しており、最適化が⼗分に適⽤されなかった旨が報告された。

2.4.6 リンク時最適化について
これはC++⾔語に限らないことだが、⼤きなプログラムは⼀般に分割コンパイルが⾏われる。 分割コンパ
イルとは、更新のあったソースコードのみオブジェクトファイルを更新し、既にコンパイル済みの他のオブジェクトファイルとリンクをして実⾏ファイルを作成することで、全体のビルド時間の短縮をはかるものである。 数値計算、特にHPC分野で使われるコードは⼤規模化が進んでおり、フルビルドに何時間もかかるものが珍しくない。 したがって、分割コンパイルを⾏うわけだが、この時に問題となるのがプロシージャ間の最適化である。簡単な例を挙げよう。

整数を受け取って、その絶対値を返す関数adjustをadjust.cppに実装する。

int adjust(int a) {if (a < 0)return -a;else return a;}

他のファイルから使えるように、関数宣⾔をadjust.hppに書いておく。

#pragma once
int adjust(int a);

これをmain関数から以下のように呼んで⾒よう。ファイルはmain.cppとする。

#include <cstdio>#include "adjust.hpp"
int main() {printf("%d\n", adjust(-5));}

さて、このコードをコンパイルする時にはadjustがどんな関数かわからないので、ただcallするしかない。
g++でコンパイルした際、対応するアセンブリはこうなる(関数名が⾒やすいようにc++ﬁltをかけてある)。

g++ -O3 main.cpp adjust.cpp

movl
call
leaq
movl
xorl
call

$-5, %edi
adjust(int)lC0(%rip), %rdi
%eax, %esi
%eax, %eax
_printf

しかし、このコードを-fltoオプションをつけてコンパイルするとg++は即値を返せるようになる。

$ g++ -O3 -flto main.cpp adjust.cpp$ objdump -S ./a.out(snip)
0000000000400500 <main>:
400500:48 83 ec 08
400504:be 05 00 00 00

sub
mov

$0x8,%rsp$0x5,%esi

400509:
40050e:400510:400515:400517:40051b:(snip)

bf 74 07 40 00
31 c0
e8 cb ff ff ff
31 c0
48 83 c4 08
c3

mov
xor
callq
xor
add
retq

$0x400774,%edi%eax,%eax
4004e0 <printf@plt>%eax,%eax$0x8,%rsp

callによる関数呼び出しが消え、adjust(-5)の実⾏結果である5がそのままprintfに渡されているのがわかる。 これは、リンク時最適化(Link time optimization, LTO)と呼ばれ、オブジェクトファイルを⽣成する
際に中間情報を保存しておき、リンク時にあらためて最適化をかけてから実⾏バイナリを作るものである。
上記のコードは、たとえば分⼦動⼒学法において周期境界条件を補正するためのコードを模したものである。 最近のコンパイラは賢いこともあり、C++プログラマは、「この程度ならインライン展開されるだろう」と期待して、⼩さな関数をたくさん作ることがある。しかし、コンパイラがLTOに対応していないと、これらは全て関数コールになってしまう。特にループの中で何回も呼び出される関数がインライン展開されないと⼤きな実⾏ペナルティとなる。
LTOに対応していないコンパイラを⽤いる場合の簡単なワークアラウンドとしては、関数の実体をヘッダに書いてしまう、という⽅法が挙げられる。先程の例であればadjust.hppの中に関数定義を書いてしまえば富⼠通コンパイラも即値を返せるようになる。しかし、なんでもかんでもヘッダファイルに⼊れてしまうと分
割コンパイルの意味がなくなってしまう。 また、いちいち「この関数はたくさん呼ばれるからヘッダファイ
ルに⼊れよう」などと考えるのも⾯倒である。
本WGでの発表時点では富⼠通コンパイラはLTOに事実上未対応であった。実際にはCコンパイラはLTOオプ
ションがあったのだが、あまり有効に機能していなかった。渡辺からLTOに対応して欲しいと要望を伝えた
ところ、千葉よりLTOが有効に機能するベンチマークがあること、コードの中⼩構⽂⽊の情報をオブジェク
トファイルに埋め込むことで、ポスト京ではLTOに対応する予定である旨の報告があった。

2.4.7 マルチスレッド環境下におけるmalloc性能について
渡辺が開発した分⼦動⼒学法コードは、並列化に疑似Flat-MPI法という⼿法を採⽤している。これはFlatMPIの計算とMPI/OpenMPハイブリッド計算が、通信部分を除いて全く同じ内容になるという特徴を持っている。したがって、通信時間が無視できる条件においては、Flat-MPIとハイブリッド計算は同じ計算資源を使うならば同じ結果を与え、かつ計算時間もほとんど変わらないはずであった。事実、別のスパコンサイト
では両者に差は⾒られなかったが、「京」においてはシングルノードで実⾏した場合でもすべてをプロセス
並列した場合と、スレッド並列した場合で、有意にマルチスレッド実⾏の⽅が遅い現象に悩まされていた。
その後の調査により、スレッド並列で実⾏される関数内部で使われているstd::vectorが性能劣化の要因であることがわかった。問題となる関数を簡略化して抜き出すと以下のような処理となる。

#pragma omp parallel for
for(int i=0;i < thread_num; i++){func(i);}

上記のように、スレッドごとに異なる引数で関数が呼ばれている。この関数の冒頭で、以下のように
std::vectorが宣⾔され、利⽤されている。

void func(int thread_index){
std::vector<int> v;//...
}

分⼦動⼒学法のホットスポットは⼒の計算であるが、このケースも90%以上が⼒の計算に費やされており、上記の場所は別のサイトであれば全体の計算時間に⽐べて無視できるほどのコストしかかからないはずである。しかし、「京」でマルチスレッド実⾏すると上記の処理が極端に遅くなり、全体で10%程度の性能劣化が⾒られた。
しかし、この箇所を、

void func(int thread_index){
thread_local std::vector<int> v;
v.clear();//...
}と、スレッドローカル宣⾔することで性能が改善し、Flat-MPIの場合とほぼ同じ実⾏時間となった。
C++⾔語を利⽤するプログラマのほとんどは、STL (Standard Template Library)を⽤いる。中でも、C⾔語の配列を置き換えるstd::vectorの利⽤頻度は⾮常に⾼い。std::vectorは、これまでプログラマが陽に⾏ってきたメモリ管理を隠蔽し、メモリの確保や解放を⾃動で⾏うことができる。その際、内部ではmalloc
やfreeといったメモリ確保、解放の関数を呼び出している。std::vectorが関数のローカル変数として使われていると、毎回malloc/freeが呼び出されることになる。しかし、thread_local宣⾔をすることで
std::vectorは暗黙にstatic修飾され、今回の実⾏条件ではfreeはプログラムの最後に⼀度だけ呼ばれるのみとなる。マルチスレッド実⾏では共有メモリとなることから、Flat-MPI実⾏に⽐べ、mallocはより⼤きな
メモリプールについて管理を⾏う必要がある。これがマルチスレッド実⾏時の性能劣化原因であろうと推定される。
上記の問題の他、⾼並列時、およそ数百ノード規模の計算を⾏った場合、Flat-MPI実⾏に⽐べてハイブリッ
ド並列実⾏が20%程度性能劣化すること、関連して、富⼠通独⾃の環境変数の役割が分かりづらいことについて報告した。性能劣化については完全な原因究明にはいたっていないが、環境変数の設定変更による性能変化の原因についてや、富⼠通独⾃のmallocの環境変数については富⼠通⼩⽥和が報告書をまとめているので参考にされたい。

2.4.8 まとめ本稿では、主に富⼠通C++コンパイラへの要望と、それに対する富⼠通の対応予定について述べた。本WGで指摘された問題点のほとんどは、ポスト京において改善される⾒通しである。よく⾔われることである
が、最適化について「銀の弾丸」は存在しない。⼀般論としては「環境を変えてもコンパイラがちゃんと最適化してくれるのでコードの可搬性が保たれる」というのは幻想に過ぎず、コードのホットスポットはハードウェアに応じてほぼ書き直す必要があるのが現状である。しかし、だからといってコンパイラの最適化機
能はおざなりにしてはならない。むしろ、プログラマがホットスポットのチューニングに注⼒するためには、コンパイラが「露払い」として、それ以外の部分をきっちり最適化できなければならない。ポスト京を取り巻く環境を概観すると、コアの増加、NUMA、SIMD幅の増加といったハードウェアの複雑化に伴ってプログラマの負担は増え、プログラミング⾔語C++もC++11、14、17と機能が追加されていき、コンパイ
ラ開発陣の負担も⼤きくなっている。プログラマも「コンパイラの最適化が⽢い」と愚痴るのみならず、出⼒されたアセンブリを確認して「どんなコードを吐いて欲しいか」を開発に伝え、開発もそのフィードバッ
クを受けてコンパイラを改善することで、ユーザと開発が両輪となり、より良いHPC環境を構築していくことが望ましい。

参考⽂献[1] "SIMD vectorization for the Lennard-Jones potential with AVX2 and AVX-512 instructions", H.
Watanabe and K. M. Nakagawa, Comput. Phys. Commun., Vol. 237, pp. 1 (2019)[2] https://github.com/kaityo256/mdacp

2.5 「京」におけるマルチスレッド malloc の問題について
富士通株式会社 次世代テクニカルコンピューティング開発本部 小田和友仁
2.5.1 はじめに「京」のメモリ管理ライブラリは、デファクトスタンダードである glibc のメモリ管理をベースに、数十 MiB〜数 GiB の大規模連続メモリを使用する HPC アプリケーションに特化
した最適化を実施している。さらに、チューニングオプションを用意し、チューニングガ
イドで挙動や選択指標などを提示してきた。しかし、メモリ利用のパターンは多様であり
チューニングの指標も多様であることから、コミュニティでチューニングに関するノウハ
ウが共有され蓄積されることが望ましい。そのためにも、利用者の声をフィードバックしチューニングガイドの成熟を図っていく。
2018 年 1 月 24 日の WG において、東京大学物性研究所の渡辺より「京」のメモリ管理ラ
イブラリの利用に関する問題提起があった（『富士通 C++コンパイラの最適化機能の改善について」の付録『「京」におけるマルチスレッド malloc』スライド）。これに対し、2019 年
2 月 25 日の WG で富士通より見解を報告し
（本書付録
『「「京」
におけるマルチスレッド malloc」
疑問点について』スライド）、改善について議論した。本報告書では、議論の結果として富士通のチューニングガイドの改善について述べる。
2.5.2 経緯と問題提起
短距離古典分子動力学方コード「MDACP」は、「京」での実行のために MPI(プロセス並列)と OpenMP(スレッド並列)を組み合わせたハイブリッド実行方式を実装に採用している。この実装において、x86 系のクラスタではプロセス並列実行とハイブリッド実行では性能差がほぼないのに対し、「京」や FX10 ではハイブリッド実行が 20%程度遅かった（図 1）。

図 1 「京」におけるハイブリッド実行と flat-MPI 実行の実行時間比較

「京」においてプロセス実行に対しハイブリッド実行で遅くなったのは、パラレル処理が終了した後のシリアル処理部であった。このことから、スレッド並列時のメモリ獲得・解放（malloc/free）の繰り返しによりプロセスヒープの空きメモリ管理リストが長くなったことで、シリアル処理部でのメモリ獲得時の空きメモリ管理リストのデフラグ（malloc̲consolidate）に時間がかかることが原因と推測した。
改善のため、スレッド毎に独立したヒープ（スレッドヒープ）を利用することで、各ヒープの空きメモリ管理リストが短くなることを期待し、XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=0（スレッドヒープを作成する）を指定し実行した（表 1）。その結果、デフォルトである
XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=1（スレッドヒープを作成しない）の場合に比べて、さらに処理時間が増加してしまった（図 2）。このことから、スレッド並列においてメモリ獲得性能優先である XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=1 に変更しても遅くなることがある点が問題提起された【問題 1】。

図 2

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=0 による効果

表 1 「京」のチューニングガイドより XOS̲MMM̲L̲ARENA̲LOCK̲TYPE の説明
変数名 (デフォルト値)

意味

用途

XOS̲MMM̲L̲ARENA

0: メモリ獲得性

メモリ割り当てポリシーに関する設定です。｢0」はメモ

̲LOCK̲TYPE (1)

能優先（malloc をリ獲得性能優先、｢1」はメモリ使用効率優先を意味しま

並列処理）す。デフォルト値は「1」。「0」「1」以外の値は「1」と1: メモリ使用効

みなします。malloc 要求が複数同時に呼び出された場

率優先（malloc を合、「0」は malloc 要求を並列処理するため mmapped ア

シリアル処理）リーナを新規作成します。このため、メモリ使用効率が低下することがあります。
「1」は逐次処理するため既存アリーナの空き検索時間が増加することがあります。

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=0（スレッドヒープ作成）による性能低下の原因を、各スレ
ッドでのメモリ獲得・解放（malloc/free）の繰り返しにより、各スレッドヒープの作成・解放（mmap/munmap）が繰り返されるためと推測した。
改善のため、作成したスレッドヒープを解放せずに再利用することを期待して、環境変数 XOS̲MMM̲L̲ARENA̲FREE=2 も合わせて指定し実行した（表 2）。その結果、スレッドヒー
プは使用せずにプロセスヒープのみを使用する場合（XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=1）と同等の処理時間となった（図 3）。このことから、XOS̲MMM̲L̲ARENA̲FREE と他の環境変数との関係が不明瞭である点が問題提起された【問題 2】また、XOS̲MMM̲L̲ARENA̲FREE という名称が機能を直感的に表していない点が問題提起された【問題 3】

図 3

XOS̲MMM̲L̲ARENA̲FREE=2 による効果

表 2 「京」のチューニングガイドより XOS̲MMM̲L̲ARENA̲FREE の説明
変数名 (デフォルト値)

意味

用途

XOS̲MMM̲L̲ARENA

1:free 時 にfree または cfree で解放されたヒープ領域の扱いに関する

̲FREE (1)

解放可能なメ

設定です。
「1」はメモリ使用効率を優先、「2」はメモリ獲得

モリページを

性能優先を意味する。デフォルト値は「1」。
「1」「2」以外の即時解放

値は「1」とみなします。

2:メモリペー「1」の場合、mmapped チャンクに確保された領域は即時解

ジを解放しな放される。128MiB 未満のサイズのメモリ要求はヒープ領域

いから割り当てられ、即時解放の対象となりません。ヒープ領域では保持している空き領域のサイズが 128MiB を超えた場
合に解放処理を行います。
「2」は 128MiB 以上のサイズのメモリ要求もヒープ領域から割り当て、解放後も空きメモリ領域として保持します。ヒー
プ領域の解放処理は行わない。メモリ使用効率低下防止のため単一のヒープ領域で全てのサイズのメモリ割り当てを行う必要があるため、スレッド固有ヒープは使用しません。

以上のように、メモリ管理ライブラリの内部動作まで踏み込んでチューニングを試みたが、富士通から提示されたチューニングガイドのメモリ管理ライブラリの情報記載には不明瞭な点があり、内部動作の解析を必要としたり、仕様誤解を招いたりする原因となった。
ポスト「京」に向けては、HPC 向けの機能の明瞭な情報が顧客に流通し、顧客間でもノウハウの共有が活発に行われることが期待される。今後の富士通としての情報流通の方針が問題提起された【問題 4】
2.5.3 問題に対する議論提起された問題は以下のとおりである。本章ではこれら問題に対する議論の結果を述べる。
【問題 1】スレッド並列でメモリ獲得性能優先である XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=0 を指定
すると遅くなる場合がある【問題 2】XOS̲MMM̲L̲ARENA̲FREE と他の環境変数との関係が不明瞭【問題 3】XOS̲MMM̲L̲ARENA̲FREE という名称が機能を直感的に表していない【問題 4】今後の富士通としての情報流通の方針
2.5.3.1【 問 題

1 】 ス レ ッ ド 並 列 で メ モ リ 獲 得 性 能 優 先 で あ る

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE=0 を指定すると遅くなる場合がある
複数スレッドで 128MiB 未満のメモリ獲得・解放（malloc/free）を繰り返すようなアプ
リケーションでは、XOS̲MMM̲L̲LOCK̲TYPE=0 を指定した場合にスレッドヒープ伸縮の繰り返
しが発生し、OS カーネルへのメモリ獲得開放（mmap/munmap）が発生することから、性能低下に繋がる可能性がある。
「京」ではヒープの収縮はアプリケーションからのメモリ解放によりヒープ領域の終端に 128MiB 連続の空き領域ができた時に発生する。各スレッドがメモリ獲得開放を繰り返す
ような状況においては、単一のプロセスヒープを共有する XOS̲MMM̲L̲LOCK̲TYPE=1 よりも各スレッドがスレッドヒープを持つ XOS̲MMM̲L̲LOCK̲TYPE=0 の方が、ヒープ伸縮回数が増加する可能性が高い。XOS̲MMM̲L̲LOCK̲TYPE=1 では複数スレッドで空き領域を取り合うため、ヒープ終端の 128MiB がどのスレッドにも利用されずにすべて空き領域となるタイミングは限られる。これに対し、XOS̲MMM̲L̲LOCK̲TYPE=0 では各スレッドが各スレッドヒープを独立利用することから、スレッドヒープに空きメモリがない状況では獲得・解放するたびに伸縮が繰り返されてしまう。

図 4

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE による終端の伸縮頻度の違いの例

上記のような状態の場合、表 3 の環境変数の組み合わせの指定によりスレッドヒープ伸縮の繰り返しの防止が期待できる。glibc の malloc では MALLOC̲TRIM̲THRESHOLD̲はプロセ
ススレッドの縮小のみに関わるが、「京」の malloc ではスレッドヒープの解放判定にも組
み込み、縮小を抑止するようにしている。
表 3 スレッド毎にヒープ利用かつメモリページを開放しないための環境変数設定
環境変数

設定値

目的

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE

0

スレッド毎にヒープを作成

MALLOC̲MMAP̲THRESHOLD̲ULONG̲MAX

mmap でメモリ獲得する閾値を最大とすることで、mmap を使用せず常にヒープか
らメモリ割り当て

MALLOC̲TRIM̲THRESHOLD̲ULONG̲MAX

ヒープ縮小の閾値を最大とすることで、ヒープを縮小せずメモリ開放しない

今回のプログラムでもスレッドヒープ伸縮の繰り返しが発生している可能性を疑い、上記の環境変数の適用を試みた。結果として、XOS̲MMM̲L̲ARENA̲LOCK̲TYPE =0 のみ設定した場合（Hybrid(lock=0)）と、さらに MALLOC̲MMAP̲THRESHOLD̲=ULONG̲MAX も指定した場合（ Hybrid(lock=0+TH) ） 、 さ ら に MALLOC̲TRIM̲THRESHOLD̲=ULONG̲MAX も 指 定 し た 場 合（Hybrid(lock=0+TH+TRIM)）はほぼ変わらぬ時間となり、効果は得られなかった（図 5）。
以上より、今回の取り組みにおいては、「京」のメモリ管理ライブラリの観点から改善の可能性を探り、環境変数の組み合わせ選択でのチューニングを試みたが、最終的にデフォルト設定を超えるような改善は果たせなかった。これ以上のチューニングには、詳細なソース解析とプロファイラによる動作解析に基づくミクロな視点からの原因調査が必要となる。

図 5 スレッド毎にヒープ利用かつメモリページを開放しないための環境変数設定の効果
2.5.3.2【問題 2】XOS̲MMM̲L̲ARENA̲FREE と他の環境変数との関係が不明瞭

XOS̲MMM̲L̲ARENA̲FREE は、
複数スレッドで 128MiB 未満のメモリ獲得・解放（malloc/free）
を繰り返すようなアプリケーションのメモリ獲得性能を改善するための環境変数設定の組
み合わせを一つの環境変数で指定可能としたものである。
表 4 に XOS̲MMM̲L̲ARENA̲FREE=2 指 定 時 に 上 書 き す る 環 境 変 数 と 設 定 値 を 示 す 。
XOS̲MMM̲L̲ARENA̲FREE=1 を設定した場合、各環境変数についてユーザ指定に従い動作する。
XOS̲MMM̲L̲ARENA̲FREE=2 を設定した場合、各環境変数を上書き設定する。これら環境変数に対し直交しないのはこのためである。
チューニングガイドにこれらの情報が明記されていなかったことが、混乱を招く原因となった。ポスト「京」ではこの環境変数は互換のために残すが、これらの情報を明記し仕様誤解を防ぐ。また、提供する環境変数について、他の環境変数との関係性が明確になる
よう、記載を見直す。
表 4

XOS̲MMM̲L̲ARENA̲FREE=2 指定時に上書きする環境変数と設定値

環境変数

上書き設定値

目的

XOS̲MMM̲L̲ARENA̲LOCK̲TYPE

1

プロセスヒープのみ使用

XOS̲MMM̲L̲MAX̲ARENA̲NUM

1

スレッド毎にヒープ作成しない

MALLOC̲MMAP̲THRESHOLD̲ULONG̲MAX

mmap でメモリ獲得する閾値を最大とする
ことで、mmap を使用せず常にヒープからメ
モリ割り当てMALLOC̲TRIM̲THRESHOLD̲ULONG̲MAX

ヒープ縮小の閾値を最大とすることで、ヒープを縮小せずメモリ開放しない2.5.3.3【問題 3】XOS̲MMM̲L̲ARENA̲FREE という名称が機能を直感的に表していない「アリーナ（スレッドヒープ）の解放」に関する設定を想起する環境変数名であるが、実際にはプロセスヒープの空きメモリ解放に関する設定であり、動作仕様に対して直感的ではない。
このような名称となったのは、動作仕様の変更によりアリーナ（スレッドヒープ）との関係が薄れたが、互換のために名称変更しなかったためである。
「京」の運用開始時は mmaped アリーナ（スレッドヒープ）を作成（XOS̲MMM̲L̲LOCK̲TYPE=0）
し、メインアリーナ（プロセスヒープ）および mmaped アリーナ（スレッドヒープ）の空き
メモリを解放しない（MALLOC̲TRIM̲THRESHOLD̲=ULONG̲MAX）仕様であった。空きメモリを
解 放 し な い こ と で mmaped ア リ ー ナ 自 体 の 解 放 を し な い こ と が 主 目 的 だ っ た た め 、「ARENA̲FREE」と名付けた。しかし、各スレッドヒープに空きメモリが分散したことで128MiB 以上の大規模連続メモリ獲得に失敗する問題が多く発生したことから、mmaped アリーナ（スレッドヒープ）を作成しない動作（XOS̲MMM̲L̲LOCK̲TYPE=1）に変更した。これにより、メインアリーナ（プロセスヒープ）の空きメモリを解放しない、という仕様だけが残った。すでに提示済みの環境変数名であったこと、アリーナでのメモリ解放（FREE）に関する設定ともとれることから、互換のために変更をしなかった。
ポスト「京」では、「京」との互換のために環境変数 XOS̲MMM̲L̲ARENA̲FREE はそのままの名称で残す。また、環境変数の組み合わせで設定することが望ましいことから、名称を改善した同等の環境変数を設けることはしない。
2.5.3.4【問題 4】今後の富士通としての情報流通の方針「京」では、HPC 向けの独自アーキテクチャに対し最適化されたメモリ管理を実現するにあたり、OS カーネルも改造し、独自実装で実現した。その結果、富士通の提示するチュー
ニングガイドが唯一の情報源であり、チューニング情報の不足が問題となった。
ポスト「京」では、富士通の独自技術による価値をいち早くお客様に提供するとともに、基盤となるソフトウェアについては汎用的な使い勝手も重視し、汎用ディストリビューシ
ョンをそのまま利用できることを目指している。最終的には、富士通が開発した HPC 向けの拡張機能も、OSS として取り込まれ、コミュニティとして情報流通していくことを目指す。
そのためにも、ユーザに積極的に HPC 向けの拡張機能を活用していただけるよう、チュー
ニングガイドの改善を図っていく。
メモリ管理ライブラリのチューニングガイドでは以下のような改善を図り、インターネ
ットなどで入手できる glibc のメモリ管理ライブラリに関する情報も合わせて活用可能とする。


ベースである glibc からの改造ポイントを明記



独自環境変数は、目的と選択指針に加え、他の環境変数（特に glibc オリジナル）との関係も明記

また、メモリ管理ライブラリの設計について、OSS 取り込みを意識し以下の観点を徹底する。
一般コミュニティに受け入れられる直感的な機能名や環境変数名とする



新規オプション作成時に既存機能の自然な拡張とし、不必要に直交性を絶たないこと2.5.4 おわりに本報告書では、WG で提起された「京」のメモリ管理ライブラリの利用に関する問題提起に対する議論の結果として、不明瞭な環境変数の仕様に対する対応方針と、チューニング
ガイドの改善方針について報告した。議論の中で、チューニングに苦労した原因として、情報の流通性が根本の問題としてあがった。ポスト京では、汎用ディストリビューション
がそのまま利用できることを目指し、富士通の改善機能も OSS に取り込まれ、コミュニテ
ィとして情報流通していくことを目指す。