第六章 (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をスローする。