概要
CentOS + devtoolset + Intel C++環境では、-O2オプションよりも-O3オプションを付けた方が計算速度が遅くなる場合がある(devtoolsetを自前コンパイルした場合は回避できるかも)。
原因はデフォルトでリンクされるlibstdc++がOS標準のものであるため。解決策としては、コンパイルオプションに-static-libstdc++を付けることでdevtoolset内のものがリンクされて計算速度も改善する。
環境
- CPU:Intel Xeon Gold 6248 (Csacadelake)
- OS: CentOS 7.8 (3.10.0-1127.19.1.el7.x86_64)
- ライブラリ:devtoolset-9, Intel MPI
- コンパイラ:Intel C++ (icpc 2021.2.0 20210228)
モチベーションと問題
科学技術計算の一つとしてMolecular Dynamics(MD)関連のC++コードをチューニングしていました1。AVX-512がバッチバチに効くようにするには、十分にinline展開されることが重要です。そこで、Intelコンパイラのオプションで-inline-factor=400などと指定して、inline展開の判断の上限を変えて計算速度の変化を見ておりました。
主なコンパイルオプションは次のようなものです。
mpiicpc -std=c++17 -O2 -ip -inline-factor=400 -no-prec-div -xCORE-AVX512 -qopt-zmm-usage=high
ちなみに、cppファイルは単一で、他はヘッダオンリーライブラリになっているので-ipと-ipoに実質的な差は無いと考えています。
ここで、-inline-factorの数値以外にも、-O2の部分を-O3に変更して計算速度を比べました。時間測定はMPI_Wtime()関数を使いました。すると次のような結果になりました(具体的コードと計算対象は省略させてもらいますがご容赦ください)。
-inline-factorの数値 |
-O2の場合の計算時間[s] |
-O3の場合の計算時間[s] |
|---|---|---|
| 100 (default) | 33.992533 | 32.275774 |
| 200 | 33.077171 | 31.213558 |
| 300 | 28.193719 | 30.905700 |
| 400 | 28.431761 | 30.406196 |
| 500 | 28.878089 | 30.671972 |
この例ですと、inline-factor=300以上からinline展開されてAVXがより効きます。それ以下では一部のループがSIMD化されません。もちろん、inline展開を過度にするとバイナリサイズが大きくなり、キャッシュの問題と相まって遅くなると予想されます。
しかし、なぜかある時点から-O3オプションでコンパイルしたものが、-O2オプションでコンパイルしたものよりも遅くなっています。
解決策
なぜ遅くなるのか原因がわからず悩みました。
-O3にすることでバイナルサイズが増えたためか?しかし本当に微増しただけでした。
-O3でinline展開やSIMD化が変わったのか?しかし主要な計算部分では殆ど変化が無いように見えます。
以前に、std::chronoが遅いという記事2を書きました。そういえば今回の時間計測でstd::chronoを使ってしまっていないか疑ったものの、上述のようにMPI_Wtimeを使っております。その記事の調査からも、MPI_Wtime()はlibstdc++のリンクには寄らず高速であることがわかっています。今回のコードではstd::chronoはどこでもcallしていません。
しかし、その試行錯誤自体はビンゴで、結果的にコンパイルオプションに-static-libstdc++を付けると次のように-O3での速度が改善しました。
-inline-factorの数値 |
-O2 -static-libstdc++での計算時間[s] |
-O3 -static-libstdc++での計算時間[s] |
|---|---|---|
| 100 (default) | 35.054073 | 32.014642 |
| 200 | 34.101968 | 31.211953 |
| 300 | 28.266521 | 27.761617 |
| 400 | 27.664209 | 27.641717 |
| 500 | 28.185959 | 27.892363 |
どうやらstd::chrono以外にもOS標準のlibstdc++では速度の出ない関数があったのだと想像できます。もしくはリンク時インライン展開(-ipoではないけど多分)で問題になるところがあったのか。そこをdevtoolset付属のものにしたことで解決したのでしょう。
ただし-O2でinline展開が十分でない場合には速度が低下しているので注意は必要です。
では原因は何なのかという話になると断言できず。この込み入ったMPIのコードから一部分を切り出して原因となる関数を特定するのはとても困難で、現状諦めたい気持ちでいっぱいです。
おわりに
原因を完全には特定せず、中途半端なレポートで申し訳ないです。MPIでのベンチは本当に魔境で辛過ぎ。そもそもinline展開の上限を上げないとSIMD化できないような私のクソクラス設計に問題があるのは否めません。ですので、とてもレアなケースかもしれません。
また、gccやclang(もしくはIntelのicpx)ならどうだという疑問もありそうですが、MPIのインストール状況などから今回は調査を断念します。
とりあえずdevtoolset環境で-O3を使う際は-static-libstdc++も併用してオプションに指定した方がよさそうです。誰かのお役に立てば幸いです。
-
正確にはMDのエンジンをコアに使ってMPMDでヘテロジニアスな計算を行うシミュレーションという特殊なものなので、一般論として今回の事例が良く起こるのかはちょっと自信がないです。 ↩