From 93860ae1766c548e970f2e84cfe3c434fda587bd Mon Sep 17 00:00:00 2001
From: "H. Watanabe" 一口に「並列化」といっても、様々な種類がありえる。一般に使われている並列プログラミングモデルは、「データ並列」「共有メモリ並列」「分散メモリ並列」の三種類であろう。 以後、プロセスやスレッドといった単語についてかなりいい加減な言葉遣いをするため、ちゃんと学びたい人はちゃんとした書籍を参考にされたい。特にWindowsとLinuxのプロセスの違いとか言い出すと話が長くなるので、ここでは説明しない。また、データ並列についてはとりあえずおいておく。 「共有メモリ並列」とは、並列単位がメモリを共有する並列化方法である。 通常は並列単位としてスレッドを用いるので、ここでは「スレッド並列」と呼ぶ。 逆に「分散メモリ並列」とは、並列単位がメモリを共有しない並列化方法である。 通常は並列単位としてプロセスを用いるので、ここでは「プロセス並列」と呼ぶ。 また、「プロセス並列」と「スレッド並列」を両方行う「ハイブリッド並列」という並列化もある。 まずはプロセスとスレッドの違いについて見てみよう。プロセスとは、OSから見た資源の管理単位である。 プロセスはOSから様々な権限を与えられるが、最も重要なことは「OSから独自のメモリ空間を割り当てられる」ことである。異なるプロセスは異なるメモリ空間を持っており、適切な権限がなければ他のプロセスのメモリを参照できない(そうしないとセキュリティ的に問題がある)。 スレッドとはCPUの利用単位である。通常、一つのCPUコアを利用できるのは一度に一つのスレッドだけである(SMTなどはさておく)。各プロセスは最低一つのスレッドを持っており、プログラムの実行とは、スレッドがCPUコアを使いにいくとである。図解するとこんな感じになる。 スレッドとはCPUの利用単位である。通常、一つのCPUコアを利用できるのは一度に一つのスレッドだけである(SMTなどはさておく)。各プロセスは最低一つのスレッドを持っており、プログラムの実行とは、スレッドがCPUコアを使いにいくことである。図解するとこんな感じになる。 となる。二次関数で、両端がゼロとなること、これが熱伝導方程式の解になっていることを確認しよう。 計算結果はこんな感じになる。 時間がたつにつれて温度が上がっていき、定常状態に近づいていくのがわかる。 計算結果はこんな感じ。 時間がたつにつれて、定常状態である直線になる。ちなみに、定常状態で温度勾配が直線になる現象はフーリエの法則という名前がついている。あのフーリエ変換のフーリエさんである。もともとフーリエは熱伝導の問題を解くためにフーリエ級数を編み出したのであった。 環境によるが、こんな画面が出てくる。 いろいろ出てきているが、とりあえずメインの計算ルーチン さて、一番重い関数はこんな感じになっている。 これを、手元のスパコンでベンチマークを取ってみよう。石はIntel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz、12コアが2ソケットで1ノードである。 まずは1ノードで、1プロセス1スレッドの計算から、スレッド数だけ2,3,4,6...と増やした場合と、プロセス数だけ2,3,4,6...と増やした場合の結果である。 左が実行時間で両対数、右が並列化効率で片対数グラフになっている。プロセス数を増やした場合、つまりflat-MPIの方が実行効率が良いことがわかる。 24プロセス計算で、シリアル計算に比べて16.6倍、つまり並列化効率にして70%程度出ている。一方、全てスレッド並列で実行した場合も、さほど悪くはないが、 24スレッド計算で、シリアル計算に比べて11.6倍、並列化効率にして48%と、50%を切ってしまう。MPIはバッファを作ったりコピーしたりという手間が発生する分、 マルチスレッドより遅くなりそうだが、今回のケースではflat-MPIの方が早かった。 今度はノードをまたいでみよう。18ノードで計算してみる。各ノードには2ソケットx12コア、合計24コアあるから、全体で18x24=432個のCPUコアがある。 これを、プログラム全体を432スレッドのまま、プロセス数を変えてみよう。例えば、flat-MPIなら1プロセスに1スレッドなので、432プロセスが最大プロセス数、 1ノードにフルにスレッドを立てると24スレッドなので、18プロセス24スレッドの計算が最小プロセス数である。 横軸にプロセス数、縦軸に実行時間をとった結果が以下の通り。 横軸は対数スケールになっている。全て432スレッドの計算で、違いは「1プロセスが何スレッドを束ねているか」だけなのだが、ここでは432プロセス計算、 すなわち1プロセスが1スレッドだけを持っているflat-MPI計算が最も早かった。 一般論として、プロセスとスレッドには最適な割合が存在し、どれが一番早いかはやってみないとわからない。 しかし、筆者の経験としては、非常に単純な計算で、かつそこそこ計算量がある場合はflat-MPIが一番早いことが多い。 ただし、筆者はスレッド並列化にあまり慣れていないため、上記のコードもOpenMPに慣れた人がチューニングしたら、もっとハイブリッド版が早くなるのかもしれない。そのあたりはいろいろ試して見てほしい。「こうすればもっと早くなった」「ここが問題だった」といったプルリクを歓迎する。MPIとは
一次元拡散方程式 (シ
一次元拡散方程式 (シ
}
OpenMPの例
perf report
calc
が計算時間の99.36%を占めるのを確認すれば良い。 このように一番「重い」関数のことを ホットスポット(hotspot) と呼ぶ。ホットスポットが90%以上を占めるような計算コードはチューニングがやりやすい。ハイブリッド並列の実例
2 2 4635 [ms]
もう少し実戦的なSIMD化
\]
磁場中の荷電粒子の運動は、磁場と平行な向きには等速直線運動、垂直な向きには円運動をするため、結果として螺旋を描いてすすむ。こんな感じ。
さて、適当な方向に向いた磁場中に、ランダムな向きに初速を持った荷電粒子たちをばらまいた系を計算してみよう。なお、粒子同士の相互作用も無視する。 三次元シミュレーションなので、三次元ベクトルを構造体で表現する。
struct vec {
@@ -464,8 +464,8 @@ もう少し実戦的なSIMD化
}
平均エネルギーの時間発展はこうなる。
1st-Eulerとあるのが1次のオイラー法である。保存するべきエネルギーがどんどん増えてしまっていることがわかる。
さて、精度を上げる数値積分法はいくらでもあるが、ここでは簡単に二次のRunge-Kutta(RK)法を採用しよう。
diff --git a/review/images/day6/multi.png b/review/images/day6/multi.png new file mode 100644 index 0000000000000000000000000000000000000000..e91d574c8b5bf2b0cef4344537c3431b86d7666c GIT binary patch literal 20981 zcmc$`1z449w>CNn3s5Aa5uJ!2Eg=mmQ$Rpax;v!1)1U-oxo8j&De3N1O6ic0RJyxs zo-tkD`o3?Uz5ji#^Phdrnd`b15_7)q`@|UcxbJ&BK~Lo*FXK_+ArOemkEKuw2n4n- z0)h4OA~yV_s#vBNzMMCZkwhWTnEyW1W`rXUHxQ3e50qTruZ(+WMHo+rZLBwN;t<$V zbX~ZHg~fZ7*H3|%r+r!>O8WJJqDEy{8rwH(&Dw_6F|9i+E<%-I%kur-rK6OJxC(9Q zKFdDfo?VG)KyWqc^LH=qw^|jG{A~D1urI=$v~Qt%{BgM|_Hg1=W59CUa-DUeN;U)B z5QVz4B{_W63%=R_Us&%Wa1e+B1Qr1T(SVK12|vRB-~GkUV(kVWtvb)c)$&PpHZ~_) z+lu)_4=ygQm#