プロセスの並列とCPUの効率性

Goでプログラムを書くと処理が早い。

ファイル処理のプログラムを書いていたのだけど
1ファイルの変換プログラムを書いたら、そのまま並列処理をさせようと設計検討をしていた。

そしたら上司から「順番は意識しなくていいのだから、1CPUで1ファイル処理させるようにしてから並列させてスループットを上げてね」と言われた。

何を言ってるのだろうと思ったけど、新しい知見が理解できたのでメモしておく。

普通のプログラムはCPUを全部使う

プログラムを実行させるとそのプログラムを終わらせるために全CPUを使って処理を行う。

ターミナルでtopコマンドを実行してみよう。

top

このとき

top -d1

とかしてやれば、1秒ごとに更新してくれる。

そんで「1」を押してみるとCPUごとの使用率が表示される。

f:id:suganoo:20180222190857p:plain

この状態のままなんかプログラムを実行すると
うわーっと全コアの使用率が上がってきて、全CPUが使われるのがわかると思う。

f:id:suganoo:20180222190847p:plain

並列化してみる

でこのファイル変換処理プログラムを並列実行させてみた。

自分がやったのは 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コアごとに処理して並列処理してた。
f:id:suganoo:20180222190839p:plain

処理性能はどうなった

こんな設定を入れたんだからスループットはどうなったの?が大事なところ。

f:id:suganoo:20180222170609p:plain

こんな風になって割り込みが減るから効率化すると思ってたんだけど

結論として、あんまり変わらなかった。
でも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プロセスあたりの処理行数が下がっていることがわかりました。

反省点とかまとめとか

  • コア数固定で並列数をあげるとスループットはどうなるかわからないけど、CPU効率は良くなりそう。
  • そもそもサーバーのスペックが良すぎるから、あまり差が出なかったのではと思ってる。
  • 1コア/1プロセスで並列化させるとCPU使用率が比例するから推測しやすく、かけ算するだけで良い。
  • なんかもうちょっと使い方をよくすればもっとスループットが出そうな気がする。

ひとまずここまで。