Goでプログラムを書くと処理が早い。
ファイル処理のプログラムを書いていたのだけど
1ファイルの変換プログラムを書いたら、そのまま並列処理をさせようと設計検討をしていた。
そしたら上司から「順番は意識しなくていいのだから、1CPUで1ファイル処理させるようにしてから並列させてスループットを上げてね」と言われた。
何を言ってるのだろうと思ったけど、新しい知見が理解できたのでメモしておく。
普通のプログラムはCPUを全部使う
プログラムを実行させるとそのプログラムを終わらせるために全CPUを使って処理を行う。
ターミナルでtopコマンドを実行してみよう。
top
このとき
top -d1
とかしてやれば、1秒ごとに更新してくれる。
そんで「1」を押してみるとCPUごとの使用率が表示される。
この状態のままなんかプログラムを実行すると
うわーっと全コアの使用率が上がってきて、全CPUが使われるのがわかると思う。
並列化してみる
でこのファイル変換処理プログラムを並列実行させてみた。
自分がやったのは goroutine でスレッド作って
それぞれの中で exec.Command(ファイル変換処理).Run() を実行して、変換処理を並列化させてみた。
もちろん1プロセスで全CPUコア使ってるのだから
並列化するとさらにもっと全CPUを使う。
CPU使用数を固定する
goにはCPU使用数を決めてくれる便利な関数がある。
import "runtime" func main() { runtime.GOMAXPROCS(1) // <---- CPU数を指定できる ..... }
runtime の runtime.GOMAXPROCS(1) を使うとCPU使用数を固定してくれるそうだ。
CPU使用数を固定して並列化
この設定をファイル変換処理プログラムに入れて並列処理させた。
するとCPUはちゃんと1コアごとに処理して並列処理してた。
処理性能はどうなった
こんな設定を入れたんだからスループットはどうなったの?が大事なところ。
こんな風になって割り込みが減るから効率化すると思ってたんだけど
結論として、あんまり変わらなかった。
でもCPU使用率は10%近く下がった。
条件を述べてなかったので書いておく
※ごめんなさい。あまり詳しく書けないです。
CPUコア数 | 32 (論理CPU:32, 物理CPU:16) |
全ファイル処理行数 | 数億行 |
ファイル数 | んーごめんわすれた。。。1000~2000ファイル?数十万行/ファイルだった気がする |
もともと1ファイルの処理性能として
全コア使用 | 1コア固定 | |
---|---|---|
処理時間 | 22秒 | 80秒 |
CPU使用率 | 13% | 3% |
だった
並列化して全データを処理した結果が下記
(いくつかのケースで検証したけど抜粋)
並列数 | CPU使用率 | 処理時間 | |
---|---|---|---|
全コアで処理 | 6パラ | 70% | 130分 |
1コア/1プロセスで処理 | 20パラ | 62% | 133分 |
↑CPU使用率を同じくらいにしようとした。
結局のところスループットはちょっと落ちたのだけど
CPU使用率が10%近く下がっていた。
個人的には「おおーめっちゃスループット出てる!」とか言いたかったけど、そうでもなかったのが残念。
# そもそもなんだけど、両ケースではファイル変換ライブラリに与えるパラメータ値も違うので正しく比較できないが、あんまり変わらなかったってことが言えればいいと思ってます。。。
並列数は物理CPU数以下がいい
この本を読み返してみるとこんなことが書いてあった。並列数を変えてやってみました。
suganoo.hatenablog.com
P246
Go 1.5からは、デフォルトでruntime.GOMAXPROCS()が設定されるようになったので、特別な場合を除いてわざわざ設定する必要はありません。しかし、最速を狙おうとすると、このデフォルト値の半分に設定するほうがスループットが上がる場合があります。現代のCPUのいくつかは、余剰のCPUリソースを使って1コアで2余剰のCPUリソースを使って1コアで2つ以上のスレッドを同時に実行する機構(ハイパースレッディングやSMT(Simultaneous Multi-Threding))を備えています。そのような機構を利用している場合、1コアで2つのヘビーな計算を同時に実行すると、CPUコアのリソースを食い合ってパフォーマンスが上がらないことがあります。
へーへーへー!!
こんなところをたまたま見つけたので並列数をいくつか変えてやってみました。
ここで書いてないんだけど1コア/1プロセスの条件下で並列数を変えて実施したところ、確かに16パラ数で実施した場合の1プロセスあたりの処理性能がが一番よかった。
物理コア数以上の並列数を実行すると1プロセスあたりの処理行数が下がっていることがわかりました。