第六章 (2): 並列化出来る/すべき箇所を見つける
1時15分就寝, 7時45分起床。
結果を表すタスク
Executorフレームワークではタスクを扱うオブジェクトとしてRunnableを使うが、Runnableは結果を返せないし、例外を投げることも出来ない。Callableを使うとこのような問題が解決される。
並列化する箇所
例えばHTMLのレンダリング処理は
- テキストの描画
- 画像のDL
を別タスクとして並列化出来る。テキストのレンダリングはCPUにべったり、画像のDLはI/Oべったりなので、分割する意味がある。
しかし、この例では分割効果はあるものの、上手くスケールアップしないという問題がある。この例ではタスクの内容が全く異質で、テキストレンダリングの方が圧倒的に早い。テキストレンダリングが画像DLの10倍早いとすれば、スケールアップの効果は僅か9%である。
タスクが互いに独立していて同質の場合は実行性能が高まる。
画像DLタスクを更に細分化することで、タスクの均質化を図る。1つ1つの画像DLを別タスクとして分け、DLが完了したら即座にレンダリングするようにすれば、実行性能が上がる。
このような完了サービスとしてはCompletionServiceを利用する。
try { for (int t = 0, n = info.size(); t < n; t++) { Future<ImageData> f = completionService.take(); // Futureはタスクが完了するまで待機する ImageData image = f.get(); renderImage(image); } } catch (InterruptedException e) { // 割り込み発生 Thread.currentThread().interrupt(); } catch (ExecutionException e) { throw launderException(e.getCause()); }
タスクに時間制限を設ける
Futureは時間制限に対応している。時間内に終わらなければTimeoutExceptioをスローする。