diff --git a/day1/index.html b/day1/index.html index 5279fd0..ae11ae2 100644 --- a/day1/index.html +++ b/day1/index.html @@ -77,7 +77,7 @@

MPIとは

一口に「並列化」といっても、様々な種類がありえる。一般に使われている並列プログラミングモデルは、「データ並列」「共有メモリ並列」「分散メモリ並列」の三種類であろう。 以後、プロセスやスレッドといった単語についてかなりいい加減な言葉遣いをするため、ちゃんと学びたい人はちゃんとした書籍を参考にされたい。特にWindowsとLinuxのプロセスの違いとか言い出すと話が長くなるので、ここでは説明しない。また、データ並列についてはとりあえずおいておく。

「共有メモリ並列」とは、並列単位がメモリを共有する並列化方法である。 通常は並列単位としてスレッドを用いるので、ここでは「スレッド並列」と呼ぶ。 逆に「分散メモリ並列」とは、並列単位がメモリを共有しない並列化方法である。 通常は並列単位としてプロセスを用いるので、ここでは「プロセス並列」と呼ぶ。 また、「プロセス並列」と「スレッド並列」を両方行う「ハイブリッド並列」という並列化もある。

まずはプロセスとスレッドの違いについて見てみよう。プロセスとは、OSから見た資源の管理単位である。 プロセスはOSから様々な権限を与えられるが、最も重要なことは「OSから独自のメモリ空間を割り当てられる」ことである。異なるプロセスは異なるメモリ空間を持っており、適切な権限がなければ他のプロセスのメモリを参照できない(そうしないとセキュリティ的に問題がある)。

-

スレッドとはCPUの利用単位である。通常、一つのCPUコアを利用できるのは一度に一つのスレッドだけである(SMTなどはさておく)。各プロセスは最低一つのスレッドを持っており、プログラムの実行とは、スレッドがCPUコアを使いにいくとである。図解するとこんな感じになる。

+

スレッドとはCPUの利用単位である。通常、一つのCPUコアを利用できるのは一度に一つのスレッドだけである(SMTなどはさておく)。各プロセスは最低一つのスレッドを持っており、プログラムの実行とは、スレッドがCPUコアを使いにいくことである。図解するとこんな感じになる。

fig/process_thread.png

fig/process_thread.png

diff --git a/day4/index.html b/day4/index.html index c9cebc1..c86c480 100644 --- a/day4/index.html +++ b/day4/index.html @@ -166,7 +166,7 @@

一次元拡散方程式 (シ

となる。二次関数で、両端がゼロとなること、これが熱伝導方程式の解になっていることを確認しよう。

計算結果はこんな感じになる。

-uniform.png +uniform.png

uniform.png

時間がたつにつれて温度が上がっていき、定常状態に近づいていくのがわかる。

@@ -184,7 +184,7 @@

一次元拡散方程式 (シ }

計算結果はこんな感じ。

-fixed.png +fixed.png

fixed.png

時間がたつにつれて、定常状態である直線になる。ちなみに、定常状態で温度勾配が直線になる現象はフーリエの法則という名前がついている。あのフーリエ変換のフーリエさんである。もともとフーリエは熱伝導の問題を解くためにフーリエ級数を編み出したのであった。

diff --git a/day6/README.md b/day6/README.md index aaccac9..9ecb7a3 100644 --- a/day6/README.md +++ b/day6/README.md @@ -234,7 +234,7 @@ perf report 環境によるが、こんな画面が出てくる。 -![perf.png](perf.png) +![fig/perf_sample.png](fig/perf_sample.png) いろいろ出てきているが、とりあえずメインの計算ルーチン`calc`が計算時間の99.36%を占めるのを確認すれば良い。 このように一番「重い」関数のことを **ホットスポット(hotspot)** と呼ぶ。ホットスポットが90%以上を占めるような計算コードはチューニングがやりやすい。 @@ -602,7 +602,7 @@ $ OMP_NUM_THREADS=2 mpiexec -np 2 ./a.out これを、手元のスパコンでベンチマークを取ってみよう。石はIntel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz、12コアが2ソケットで1ノードである。 まずは1ノードで、1プロセス1スレッドの計算から、スレッド数だけ2,3,4,6...と増やした場合と、プロセス数だけ2,3,4,6...と増やした場合の結果である。 -![single_scaling.png](single_scaling.png) +![fig/single_scaling.png](fig/single_scaling.png) 左が実行時間で両対数、右が並列化効率で片対数グラフになっている。プロセス数を増やした場合、つまりflat-MPIの方が実行効率が良いことがわかる。 24プロセス計算で、シリアル計算に比べて16.6倍、つまり並列化効率にして70%程度出ている。一方、全てスレッド並列で実行した場合も、さほど悪くはないが、 @@ -614,7 +614,7 @@ $ OMP_NUM_THREADS=2 mpiexec -np 2 ./a.out 1ノードにフルにスレッドを立てると24スレッドなので、18プロセス24スレッドの計算が最小プロセス数である。 横軸にプロセス数、縦軸に実行時間をとった結果が以下の通り。 -![multi.png](multi.png) +![fig/multi.png](fig/multi.png) 横軸は対数スケールになっている。全て432スレッドの計算で、違いは「1プロセスが何スレッドを束ねているか」だけなのだが、ここでは432プロセス計算、 すなわち1プロセスが1スレッドだけを持っているflat-MPI計算が最も早かった。 diff --git a/day6/multi.png b/day6/fig/multi.png similarity index 100% rename from day6/multi.png rename to day6/fig/multi.png diff --git a/day6/perf.png b/day6/fig/perf_sample.png similarity index 100% rename from day6/perf.png rename to day6/fig/perf_sample.png diff --git a/day6/single_scaling.png b/day6/fig/single_scaling.png similarity index 100% rename from day6/single_scaling.png rename to day6/fig/single_scaling.png diff --git a/day6/index.html b/day6/index.html index 2720519..2ecc231 100644 --- a/day6/index.html +++ b/day6/index.html @@ -193,8 +193,8 @@

OpenMPの例

perf report

環境によるが、こんな画面が出てくる。

-perf.png -

perf.png

+fig/perf_sample.png +

fig/perf_sample.png

いろいろ出てきているが、とりあえずメインの計算ルーチンcalcが計算時間の99.36%を占めるのを確認すれば良い。 このように一番「重い」関数のことを ホットスポット(hotspot) と呼ぶ。ホットスポットが90%以上を占めるような計算コードはチューニングがやりやすい。

さて、一番重い関数はこんな感じになっている。

@@ -422,14 +422,14 @@

ハイブリッド並列の実例

2 2 4635 [ms]

これを、手元のスパコンでベンチマークを取ってみよう。石はIntel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz、12コアが2ソケットで1ノードである。 まずは1ノードで、1プロセス1スレッドの計算から、スレッド数だけ2,3,4,6...と増やした場合と、プロセス数だけ2,3,4,6...と増やした場合の結果である。

-single_scaling.png -

single_scaling.png

+fig/single_scaling.png +

fig/single_scaling.png

左が実行時間で両対数、右が並列化効率で片対数グラフになっている。プロセス数を増やした場合、つまり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スレッドの計算が最小プロセス数である。 横軸にプロセス数、縦軸に実行時間をとった結果が以下の通り。

-multi.png -

multi.png

+fig/multi.png +

fig/multi.png

横軸は対数スケールになっている。全て432スレッドの計算で、違いは「1プロセスが何スレッドを束ねているか」だけなのだが、ここでは432プロセス計算、 すなわち1プロセスが1スレッドだけを持っているflat-MPI計算が最も早かった。

一般論として、プロセスとスレッドには最適な割合が存在し、どれが一番早いかはやってみないとわからない。 しかし、筆者の経験としては、非常に単純な計算で、かつそこそこ計算量がある場合はflat-MPIが一番早いことが多い。 ただし、筆者はスレッド並列化にあまり慣れていないため、上記のコードもOpenMPに慣れた人がチューニングしたら、もっとハイブリッド版が早くなるのかもしれない。そのあたりはいろいろ試して見てほしい。「こうすればもっと早くなった」「ここが問題だった」といったプルリクを歓迎する。

diff --git a/day7/README.md b/day7/README.md index 63dab58..2131819 100644 --- a/day7/README.md +++ b/day7/README.md @@ -591,7 +591,7 @@ $$ 磁場中の荷電粒子の運動は、磁場と平行な向きには等速直線運動、垂直な向きには円運動をするため、結果として螺旋を描いてすすむ。こんな感じ。 -![magnetic/one.png](magnetic/one.png) +![fig/one.png](fig/one.png) さて、適当な方向に向いた磁場中に、ランダムな向きに初速を持った荷電粒子たちをばらまいた系を計算してみよう。なお、粒子同士の相互作用も無視する。 三次元シミュレーションなので、三次元ベクトルを構造体で表現する。 @@ -657,7 +657,7 @@ double energy(void) { 平均エネルギーの時間発展はこうなる。 -![magnetic/energy.png](magnetic/energy.png) +![fig/energy.png](fig/energy.png) 1st-Eulerとあるのが1次のオイラー法である。保存するべきエネルギーがどんどん増えてしまっていることがわかる。 diff --git a/day7/magnetic/energy.png b/day7/fig/energy.png similarity index 100% rename from day7/magnetic/energy.png rename to day7/fig/energy.png diff --git a/day7/magnetic/one.png b/day7/fig/one.png similarity index 100% rename from day7/magnetic/one.png rename to day7/fig/one.png diff --git a/day7/index.html b/day7/index.html index bedf982..d290c89 100644 --- a/day7/index.html +++ b/day7/index.html @@ -418,8 +418,8 @@

もう少し実戦的なSIMD化

\]

磁場中の荷電粒子の運動は、磁場と平行な向きには等速直線運動、垂直な向きには円運動をするため、結果として螺旋を描いてすすむ。こんな感じ。

-magnetic/one.png -

magnetic/one.png

+fig/one.png +

fig/one.png

さて、適当な方向に向いた磁場中に、ランダムな向きに初速を持った荷電粒子たちをばらまいた系を計算してみよう。なお、粒子同士の相互作用も無視する。 三次元シミュレーションなので、三次元ベクトルを構造体で表現する。

struct vec {
@@ -464,8 +464,8 @@ 

もう少し実戦的なSIMD化

}

平均エネルギーの時間発展はこうなる。

-magnetic/energy.png -

magnetic/energy.png

+fig/energy.png +

fig/energy.png

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 0000000..e91d574 Binary files /dev/null and b/review/images/day6/multi.png differ diff --git a/review/images/day6/perf_sample.png b/review/images/day6/perf_sample.png new file mode 100644 index 0000000..84d8c9b Binary files /dev/null and b/review/images/day6/perf_sample.png differ diff --git a/review/images/day6/single_scaling.png b/review/images/day6/single_scaling.png new file mode 100644 index 0000000..9d5dd46 Binary files /dev/null and b/review/images/day6/single_scaling.png differ diff --git a/review/images/day7/energy.png b/review/images/day7/energy.png new file mode 100644 index 0000000..c4b914b Binary files /dev/null and b/review/images/day7/energy.png differ diff --git a/review/images/day7/one.png b/review/images/day7/one.png new file mode 100644 index 0000000..2ad1a73 Binary files /dev/null and b/review/images/day7/one.png differ diff --git a/review/makefile b/review/makefile index 497b5fc..ed7ea46 100644 --- a/review/makefile +++ b/review/makefile @@ -1,6 +1,16 @@ all: sevendayshpc.pdf RE=preface.re day1.re day2.re day3.re day4.re day5.re day6.re day7.re postface.re +imgcopy: + cp ../preface/fig/*.png images/preface + cp ../day1/fig/*.png images/day1 + cp ../day2/fig/*.png images/day2 + cp ../day3/fig/*.png images/day3 + cp ../day4/fig/*.png images/day4 + cp ../day5/fig/*.png images/day5 + cp ../day6/fig/*.png images/day6 + cp ../day7/fig/*.png images/day7 + preface.md: ../preface/README.md cp $< $@ @@ -35,7 +45,8 @@ day7.md: ../day7/README.md ruby post.rb $*.post > $*.re sevendayshpc.pdf: config.yml $(RE) - docker run -it --rm -v C:\\Users\\watanabe\\Desktop\\github\\sevendayshpc\\review:/work kauplan/review2.5 /bin/bash -c "cd /work;rake pdf" + docker run --rm -v `pwd`/:/work kauplan/review2.5 /bin/bash -c "cd /work; rake pdf" + #docker run -it --rm -v C:\\Users\\watanabe\\Desktop\\github\\sevendayshpc\\review:/work kauplan/review2.5 /bin/bash -c "cd /work;rake pdf" clean: