Javaでフォーク/ジョインやったらReal時間 < CPU時間になった

現場でマルチスレッド処理を手がけており、それ関係のFWを触っていたら面白いことを知ったのでメモ。

Javaのマルチスレッド FWとは

Javaでは昔からマルチスレッドでプログラミングするためのクラスが用意されていたが、Java5とJava7から新しいFWが出てきた。

f:id:hiroga_cc:20171129221704j:plain

整理すると...
Java5で実装
- タスクの非同期処理周りを隠蔽するExecutorService
- ↑のインターフェースの実装クラスを生成するExecutorsクラスとnew***(); メソッド
Java7で実装
- ExecutorServiceインターフェースを実装したForkJoinPoolなどのクラス。newで生成可能。

トランザクションを非同期で発行するだけならJava5までのやり方のほうが(オーバーヘッドがなくて)よく、 再帰的な(=スレッドを無数に生成する)処理ならフォーク/ジョインを使うのがよいらしい。

参考:
multithreading - Java's Fork/Join vs ExecutorService - when to use which? - Stack Overflow

今回はJava7で追加された前者でいきます。

パフォーマンス測定手法

マルチスレッドのプログラムのCPU時間の測り方を見るのが目的!
フィボナッチ数列をシングルスレッド/マルチスレッドでそれぞれ計算するプログラムを作りました。
これらをターミナルから実行し、timeコマンドで測定します。
シングルスレッドとマルチスレッドの性能比較ではないので悪しからず

作ったプログラム

シングルスレッド版
gist.github.com

マルチスレッド版
gist.github.com

計測結果

$ time java Fib
1134903170(←注:計算結果)

real    0m7.859s
user    0m7.429s
sys 0m0.167s

$ time java Fib7
1134903170

real    0m30.679s
user    1m40.597s
sys 0m0.863s

シングルスレッドではreal ≒ user + sys なのに対し、マルチスレッドでは real > user + sys である。これはどういうことか。

なぜReal時間 < CPU時間なのか

要するに各スレッドで使用したCPU時間の合計が表示されるらしい。

参考にさせていただいた記事。
real time/user CPU time/system CPU timeの違いをメモ | Siguniang's Blog

参考資料

Oracleさんのドキュメント

Executors (Java Platform SE 7)

ExecutorService (Java Platform SE 7)

ForkJoinPool (Java Platform SE 8)

実装の参考にさせていただいたブログ記事

ExecutorServiceを使って、Javaでマルチスレッド処理 - Qiita

Java で簡単マルチスレッドプログラミング - にょきにょきブログ

Fork/Join Frameworkでフィボナッチ数を求める - Qiita