『Re: InputStreamからStringへの変換』の記事のコードはNIO使えばもっと速くなるんじゃないかと思ってベンチマーク取ってみた
言及対象の記事
この記事は当時Javaのことが何もわかっていなかった自分にとって大変助かった記事だし、 その後仕事でも度々助けられました。本当に感謝してます。
あの記事では自分が当時Java初心者だったこともあって、とても分かりやすいコードを書いてくれたのだけど、 ふとNIOを使ったほうが速くなるんじゃないかと思ったので試してみた。 …軽い気持ちで試してみようと思ったつもりが、異常値弾いてちゃんとベンチマークするまで結構時間がかかってしまった。 正直最後の結果でもちゃんとベンチマークできているか不安なので、コードにforkなりコメント欄なりツッコミをくれるとうれしいです。
TL;DR
先に結果だけ貼っておきます。なおコードは長いので記事の最後にあります。 (100回各メソッドを実行してそのうちの30回の中央値を取るベンチマークプログラムを3回実行した結果)
Result: convertInputStreamToString() (Median 30/100) : 42811 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 41121 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 36258 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 36126 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 45299 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45375 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 42545 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 40247 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35423 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 34986 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 43989 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45603 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 43343 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 41216 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35727 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 35233 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 45869 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45755 nanoseconds
比較対象
- thincaさんが教えてくれたメソッド (
convertInputStreamToString()
) - thincaさんが教えてくれたメソッドをCharset指定できるようにしたメソッド (
convertInputStreamToStringWithCharset()
) - NIO使うようにしたメソッド(
allocate()
版) (convertInputStreamToStringNio()
) - NIO使うようにしたメソッド(
allocate()
版)をCharset指定できるようにしたメソッド (convertInputStreamToStringNioWithCharset()
) - NIO使うようにしたメソッド(
allocateDirect()
版) (convertInputStreamToStringNioDirectBuffer()
) - NIO使うようにしたメソッド(
allocateDirect()
版)をCharset指定できるようにしたメソッド (convertInputStreamToStringNioDirectBufferWithCharset()
)
実行環境
- Eclipse + JDK 1.8.0_40
- テスト用ファイル:「0」の文字をひたすら32MiB分書き込んだファイル
それぞれの比較対象メソッド(new FileInputStream("テスト用ファイル"));
1回だけだとファイルの内容がOSのキャッシュに乗ってないと思ったので2回ループして試した結果
(COUNT = 10_0000, RESULT_TIME_UNIT = TimeUnit.MILLISECONDS)
Result: convertInputStreamToString() : 4023 milliseconds convertInputStreamToStringWithCharset() : 4072 milliseconds convertInputStreamToStringNio() : 3144 milliseconds convertInputStreamToStringNioWithCharset() : 3859 milliseconds Result: convertInputStreamToString() : 4283 milliseconds convertInputStreamToStringWithCharset() : 4144 milliseconds convertInputStreamToStringNio() : 3155 milliseconds convertInputStreamToStringNioWithCharset() : 4267 milliseconds
ループした時間を求めるのではなく個別に計測した結果を表示させてみた
やはり何回か実行すると値にばらつきがあり、正しい計測ができていないと感じたので、 COUNT回ループした時間を求めるのではなく、個別に計測してすべての値を表示させてみた。 案の定異常値がちらほら見られた。最初にとても遅いのは冒頭でも言ったようにファイルの内容がOSのキャッシュに乗っていないためと思われる。 (COUNT = 100, RESULT_TIME_UNIT = TimeUnit.NANOSECONDS)
各メソッドを100回実行した結果を全て載せていて長いので注意。
Result: convertInputStreamToString() : 1739653710 nanoseconds convertInputStreamToString() : 329345 nanoseconds convertInputStreamToString() : 67237 nanoseconds convertInputStreamToString() : 51282 nanoseconds convertInputStreamToString() : 50142 nanoseconds convertInputStreamToString() : 47863 nanoseconds convertInputStreamToString() : 49572 nanoseconds convertInputStreamToString() : 47293 nanoseconds convertInputStreamToString() : 50142 nanoseconds convertInputStreamToString() : 49003 nanoseconds convertInputStreamToString() : 49003 nanoseconds convertInputStreamToString() : 48433 nanoseconds convertInputStreamToString() : 50143 nanoseconds convertInputStreamToString() : 48433 nanoseconds convertInputStreamToString() : 49573 nanoseconds convertInputStreamToString() : 47863 nanoseconds convertInputStreamToString() : 50142 nanoseconds convertInputStreamToString() : 50712 nanoseconds convertInputStreamToString() : 52421 nanoseconds convertInputStreamToString() : 50142 nanoseconds convertInputStreamToString() : 52422 nanoseconds convertInputStreamToString() : 48433 nanoseconds convertInputStreamToString() : 49572 nanoseconds convertInputStreamToString() : 49573 nanoseconds convertInputStreamToString() : 50713 nanoseconds convertInputStreamToString() : 51282 nanoseconds convertInputStreamToString() : 50712 nanoseconds convertInputStreamToString() : 51851 nanoseconds convertInputStreamToString() : 50142 nanoseconds convertInputStreamToString() : 47863 nanoseconds convertInputStreamToString() : 52421 nanoseconds convertInputStreamToString() : 51282 nanoseconds convertInputStreamToString() : 246154 nanoseconds convertInputStreamToString() : 51852 nanoseconds convertInputStreamToString() : 47863 nanoseconds convertInputStreamToString() : 52422 nanoseconds convertInputStreamToString() : 131624 nanoseconds convertInputStreamToString() : 68946 nanoseconds convertInputStreamToString() : 58119 nanoseconds convertInputStreamToString() : 56980 nanoseconds convertInputStreamToString() : 64957 nanoseconds convertInputStreamToString() : 62108 nanoseconds convertInputStreamToString() : 69516 nanoseconds convertInputStreamToString() : 61538 nanoseconds convertInputStreamToString() : 62679 nanoseconds convertInputStreamToString() : 61538 nanoseconds convertInputStreamToString() : 62678 nanoseconds convertInputStreamToString() : 56980 nanoseconds convertInputStreamToString() : 58119 nanoseconds convertInputStreamToString() : 59259 nanoseconds convertInputStreamToString() : 54701 nanoseconds convertInputStreamToString() : 55841 nanoseconds convertInputStreamToString() : 55841 nanoseconds convertInputStreamToString() : 54131 nanoseconds convertInputStreamToString() : 54700 nanoseconds convertInputStreamToString() : 56980 nanoseconds convertInputStreamToString() : 59829 nanoseconds convertInputStreamToString() : 59259 nanoseconds convertInputStreamToString() : 58690 nanoseconds convertInputStreamToString() : 418803 nanoseconds convertInputStreamToString() : 90028 nanoseconds convertInputStreamToString() : 59260 nanoseconds convertInputStreamToString() : 56410 nanoseconds convertInputStreamToString() : 54701 nanoseconds convertInputStreamToString() : 60969 nanoseconds convertInputStreamToString() : 59829 nanoseconds convertInputStreamToString() : 63818 nanoseconds convertInputStreamToString() : 70655 nanoseconds convertInputStreamToString() : 59259 nanoseconds convertInputStreamToString() : 60969 nanoseconds convertInputStreamToString() : 43875 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 38177 nanoseconds convertInputStreamToString() : 39316 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToString() : 39316 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToString() : 38176 nanoseconds convertInputStreamToString() : 40456 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 38747 nanoseconds convertInputStreamToString() : 41596 nanoseconds convertInputStreamToString() : 38177 nanoseconds convertInputStreamToString() : 38746 nanoseconds convertInputStreamToString() : 41025 nanoseconds convertInputStreamToString() : 38746 nanoseconds convertInputStreamToString() : 38746 nanoseconds convertInputStreamToString() : 41026 nanoseconds convertInputStreamToString() : 40456 nanoseconds convertInputStreamToString() : 42165 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToString() : 53561 nanoseconds convertInputStreamToString() : 39316 nanoseconds convertInputStreamToString() : 42165 nanoseconds convertInputStreamToString() : 39886 nanoseconds convertInputStreamToStringWithCharset() : 1651830398 nanoseconds convertInputStreamToStringWithCharset() : 384615 nanoseconds convertInputStreamToStringWithCharset() : 105413 nanoseconds convertInputStreamToStringWithCharset() : 74644 nanoseconds convertInputStreamToStringWithCharset() : 62678 nanoseconds convertInputStreamToStringWithCharset() : 60969 nanoseconds convertInputStreamToStringWithCharset() : 56410 nanoseconds convertInputStreamToStringWithCharset() : 54701 nanoseconds convertInputStreamToStringWithCharset() : 60968 nanoseconds convertInputStreamToStringWithCharset() : 70655 nanoseconds convertInputStreamToStringWithCharset() : 70086 nanoseconds convertInputStreamToStringWithCharset() : 71225 nanoseconds convertInputStreamToStringWithCharset() : 67806 nanoseconds convertInputStreamToStringWithCharset() : 70655 nanoseconds convertInputStreamToStringWithCharset() : 70655 nanoseconds convertInputStreamToStringWithCharset() : 116239 nanoseconds convertInputStreamToStringWithCharset() : 187464 nanoseconds convertInputStreamToStringWithCharset() : 74074 nanoseconds convertInputStreamToStringWithCharset() : 61538 nanoseconds convertInputStreamToStringWithCharset() : 60968 nanoseconds convertInputStreamToStringWithCharset() : 58689 nanoseconds convertInputStreamToStringWithCharset() : 56980 nanoseconds convertInputStreamToStringWithCharset() : 58120 nanoseconds convertInputStreamToStringWithCharset() : 60968 nanoseconds convertInputStreamToStringWithCharset() : 58689 nanoseconds convertInputStreamToStringWithCharset() : 62108 nanoseconds convertInputStreamToStringWithCharset() : 60399 nanoseconds convertInputStreamToStringWithCharset() : 58119 nanoseconds convertInputStreamToStringWithCharset() : 60399 nanoseconds convertInputStreamToStringWithCharset() : 72935 nanoseconds convertInputStreamToStringWithCharset() : 67236 nanoseconds convertInputStreamToStringWithCharset() : 62109 nanoseconds convertInputStreamToStringWithCharset() : 62678 nanoseconds convertInputStreamToStringWithCharset() : 60399 nanoseconds convertInputStreamToStringWithCharset() : 65527 nanoseconds convertInputStreamToStringWithCharset() : 63818 nanoseconds convertInputStreamToStringWithCharset() : 282621 nanoseconds convertInputStreamToStringWithCharset() : 43304 nanoseconds convertInputStreamToStringWithCharset() : 37607 nanoseconds convertInputStreamToStringWithCharset() : 37606 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 35897 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 37607 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 38177 nanoseconds convertInputStreamToStringWithCharset() : 34758 nanoseconds convertInputStreamToStringWithCharset() : 127635 nanoseconds convertInputStreamToStringWithCharset() : 87749 nanoseconds convertInputStreamToStringWithCharset() : 67806 nanoseconds convertInputStreamToStringWithCharset() : 56411 nanoseconds convertInputStreamToStringWithCharset() : 59829 nanoseconds convertInputStreamToStringWithCharset() : 55270 nanoseconds convertInputStreamToStringWithCharset() : 56980 nanoseconds convertInputStreamToStringWithCharset() : 53561 nanoseconds convertInputStreamToStringWithCharset() : 62678 nanoseconds convertInputStreamToStringWithCharset() : 54701 nanoseconds convertInputStreamToStringWithCharset() : 51851 nanoseconds convertInputStreamToStringWithCharset() : 62108 nanoseconds convertInputStreamToStringWithCharset() : 61538 nanoseconds convertInputStreamToStringWithCharset() : 51852 nanoseconds convertInputStreamToStringWithCharset() : 38177 nanoseconds convertInputStreamToStringWithCharset() : 37607 nanoseconds convertInputStreamToStringWithCharset() : 39316 nanoseconds convertInputStreamToStringWithCharset() : 37606 nanoseconds convertInputStreamToStringWithCharset() : 35898 nanoseconds convertInputStreamToStringWithCharset() : 35898 nanoseconds convertInputStreamToStringWithCharset() : 34758 nanoseconds convertInputStreamToStringWithCharset() : 35327 nanoseconds convertInputStreamToStringWithCharset() : 40456 nanoseconds convertInputStreamToStringWithCharset() : 37606 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 35327 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 36468 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 34757 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 38177 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 38746 nanoseconds convertInputStreamToStringWithCharset() : 37607 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 38746 nanoseconds convertInputStreamToStringWithCharset() : 40456 nanoseconds convertInputStreamToStringWithCharset() : 38176 nanoseconds convertInputStreamToStringWithCharset() : 37037 nanoseconds convertInputStreamToStringWithCharset() : 38746 nanoseconds convertInputStreamToStringWithCharset() : 38176 nanoseconds convertInputStreamToStringWithCharset() : 35897 nanoseconds convertInputStreamToStringWithCharset() : 39316 nanoseconds convertInputStreamToStringWithCharset() : 39886 nanoseconds convertInputStreamToStringWithCharset() : 36467 nanoseconds convertInputStreamToStringNio() : 1565111359 nanoseconds convertInputStreamToStringNio() : 370371 nanoseconds convertInputStreamToStringNio() : 100285 nanoseconds convertInputStreamToStringNio() : 59259 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 54701 nanoseconds convertInputStreamToStringNio() : 55271 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 55271 nanoseconds convertInputStreamToStringNio() : 52422 nanoseconds convertInputStreamToStringNio() : 54132 nanoseconds convertInputStreamToStringNio() : 55841 nanoseconds convertInputStreamToStringNio() : 55271 nanoseconds convertInputStreamToStringNio() : 53561 nanoseconds convertInputStreamToStringNio() : 55270 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 52422 nanoseconds convertInputStreamToStringNio() : 52992 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 52991 nanoseconds convertInputStreamToStringNio() : 47863 nanoseconds convertInputStreamToStringNio() : 51282 nanoseconds convertInputStreamToStringNio() : 52422 nanoseconds convertInputStreamToStringNio() : 51851 nanoseconds convertInputStreamToStringNio() : 52991 nanoseconds convertInputStreamToStringNio() : 52991 nanoseconds convertInputStreamToStringNio() : 51852 nanoseconds convertInputStreamToStringNio() : 54131 nanoseconds convertInputStreamToStringNio() : 51282 nanoseconds convertInputStreamToStringNio() : 50713 nanoseconds convertInputStreamToStringNio() : 51282 nanoseconds convertInputStreamToStringNio() : 49572 nanoseconds convertInputStreamToStringNio() : 50712 nanoseconds convertInputStreamToStringNio() : 51852 nanoseconds convertInputStreamToStringNio() : 52991 nanoseconds convertInputStreamToStringNio() : 50712 nanoseconds convertInputStreamToStringNio() : 51852 nanoseconds convertInputStreamToStringNio() : 53561 nanoseconds convertInputStreamToStringNio() : 68376 nanoseconds convertInputStreamToStringNio() : 191453 nanoseconds convertInputStreamToStringNio() : 58690 nanoseconds convertInputStreamToStringNio() : 55271 nanoseconds convertInputStreamToStringNio() : 52992 nanoseconds convertInputStreamToStringNio() : 248433 nanoseconds convertInputStreamToStringNio() : 38177 nanoseconds convertInputStreamToStringNio() : 33049 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31908 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31908 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31908 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31908 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31908 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 31909 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNio() : 33048 nanoseconds convertInputStreamToStringNio() : 32478 nanoseconds convertInputStreamToStringNio() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 2269505830 nanoseconds convertInputStreamToStringNioWithCharset() : 357835 nanoseconds convertInputStreamToStringNioWithCharset() : 62678 nanoseconds convertInputStreamToStringNioWithCharset() : 39886 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 37607 nanoseconds convertInputStreamToStringNioWithCharset() : 103134 nanoseconds convertInputStreamToStringNioWithCharset() : 41026 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38176 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38747 nanoseconds convertInputStreamToStringNioWithCharset() : 38177 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 39316 nanoseconds convertInputStreamToStringNioWithCharset() : 38746 nanoseconds convertInputStreamToStringNioWithCharset() : 222792 nanoseconds convertInputStreamToStringNioWithCharset() : 36467 nanoseconds convertInputStreamToStringNioWithCharset() : 33048 nanoseconds convertInputStreamToStringNioWithCharset() : 33049 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31908 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31908 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 33049 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 33049 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 32478 nanoseconds convertInputStreamToStringNioWithCharset() : 33048 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds convertInputStreamToStringNioWithCharset() : 32479 nanoseconds convertInputStreamToStringNioWithCharset() : 31909 nanoseconds
初回やたまに出てくる異常値を捨てるため、中央値(と言ってもいいのかわからんけど)を取るようにした。 今回は100回実行した結果から30回分の結果を取得し、その平均を求めることにした。 (これ中央値って言っていいのか…?統計詳しい人のツッコミが欲しい) (COUNT = 100, Median_SAMPLE_NUM = 30, RESULT_TIME_UNIT = TimeUnit.NANOSECONDS)
Result: (個別の結果は省略) convertInputStreamToString() (Median 30/100) : 43058 nanoseconds (個別の結果は省略) convertInputStreamToStringWithCharset() (Median 30/100) : 41026 nanoseconds (個別の結果は省略) convertInputStreamToStringNio() (Median 30/100) : 31871 nanoseconds (個別の結果は省略) convertInputStreamToStringNioWithCharset() (Median 30/100) : 34245 nanoseconds
この後3回程実行してみたが、charset指定のが速い時があった。 これはむしろ予測通りというか、Javaの内部エンコーディングはUTF-16なのでUTF-16を指定すれば変換処理が実行されないと思ったからだ。 (そのためにUTF-16を指定した)
Result: convertInputStreamToString() (Median 30/100) : 43324 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 38272 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 34568 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 32213 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 41709 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 42716 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 34587 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 34473 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 40551 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 41500 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35461 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 34150 nanoseconds
ようやくベンチマーク環境が整ったのでちょっと興味があったallocateDirect()版も試してみた。 allocateDirect(int)はallocate(int)と違ってJVMのスタックにアロケーションする。 ただallocate(int)をallocateDirect(int)に単純に置き換えただけだと次の例外が飛んだ。
java.lang.UnsupportedOperationException at java.nio.ByteBuffer.array(ByteBuffer.java:994) at TestMain.convertInputStreamToStringNioDirectBuffer(TestMain.java:194) at TestMain$5.run(TestMain.java:52) at TestMain$Task.start(TestMain.java:93) at TestMain.main(TestMain.java:65)
該当箇所は以下の通り。
193: while (0 <= (numRead = channel.read(buf))) { 194: result.write(buf.array(), 0, numRead); 195: buf.clear(); 196: }
どうやらbuf.array()
はallocateDirect()
を使うと使えないらしい。
まぁ確かにスタックへのアクセスを許してしまったら良からぬことが起きそうではある。
あとこれも予想だけど、単にJavaのオブジェクト(byte[]オブジェクト)が用意されていないだけのような気もする。
ReadableByteChannel#hasArray()
もfalseを返した。
そこで以下のように書き換えた。
static String convertInputStreamToStringNioDirectBuffer(InputStream is) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); ReadableByteChannel channel = Channels.newChannel(is); ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); byte[] rawBuf = new byte[BUFFER_SIZE]; int numRead; while (0 <= (numRead = channel.read(buf))) { buf.get(rawBuf, -buf.position(), numRead); result.write(rawBuf, 0, numRead); buf.clear(); } return result.toString(); } java.lang.IndexOutOfBoundsException at java.nio.Buffer.checkBounds(Buffer.java:567) at java.nio.DirectByteBuffer.get(DirectByteBuffer.java:265) at TestMain.convertInputStreamToStringNioDirectBuffer(TestMain.java:198) at TestMain$5.run(TestMain.java:52) at TestMain$Task.start(TestMain.java:94) at TestMain.main(TestMain.java:66)
がーん、だな…出鼻をくじかれた
という事でByteBufferの使い方がまだよく分かってないことが分かったので、
気にせずもう少しNIOのコードを速度最適化することにした。
というのもByteBuffer
のAPIを見ていたらByteBuffer#clear()
は単純にpositionをリセットするのではなく、バッファの再割り当てを行うとか書いてあったのを目にしたから。
Buffer#clear() (Java Platform SE 6)
このバッファーをクリアします。バッファーの位置は 0、リミットは容量の値に設定されます。 マークは破棄されます。 一連のチャネル読み込み操作または「put」操作を使用してこのバッファーにデータを格納する前に、このメソッドを呼び出します。例を示します。 buf.clear(); // Prepare buffer for reading in.read(buf); // Read data このメソッドはバッファー内のデータを実際に消去するわけではありません。 しかし、そうした状況で使用されるため、クリア (clear) と命名されています。
目にした…と思ったけどそうでもなかったかもしれない。
ただ確実にByteBuffer#reset()
のが軽い処理(恐らくpositionの値しか変えない?)なはずだしそちらを使ってみる。
(以下にByteBuffer#reset()
のjavadocを引用する)
Buffer#reset() (Java Platform SE 6)
バッファーの位置を以前にマークした位置に戻します。 このメソッドを呼び出しても、マークの値は変更されません。 マークが破棄されることもありません。
そして置き換えた後の結果がこちら。
Result: convertInputStreamToString() (Median 30/100) : 43400 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 40285 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35365 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 35480 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 42260 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 40779 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35822 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 35252 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 43077 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 40760 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35555 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 35100 nanoseconds
そしてByteBuffer#reset()
を知ったので先ほど例外が出ていた
convertInputStreamToStringNioDirectBuffer()
convertInputStreamToStringNioDirectBufferWithCharset()
をちゃんと実装する事ができた。
static String convertInputStreamToStringNioDirectBuffer(InputStream is) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); ReadableByteChannel channel = Channels.newChannel(is); ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); buf.mark(); byte[] rawBuf = new byte[BUFFER_SIZE]; int numRead; while (0 <= (numRead = channel.read(buf))) { buf.reset(); buf.get(rawBuf, 0, numRead); result.write(rawBuf, 0, numRead); buf.reset(); } return result.toString(); }
Result: convertInputStreamToString() (Median 30/100) : 42811 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 41121 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 36258 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 36126 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 45299 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45375 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 42545 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 40247 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35423 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 34986 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 43989 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45603 nanoseconds Result: convertInputStreamToString() (Median 30/100) : 43343 nanoseconds convertInputStreamToStringWithCharset() (Median 30/100) : 41216 nanoseconds convertInputStreamToStringNio() (Median 30/100) : 35727 nanoseconds convertInputStreamToStringNioWithCharset() (Median 30/100) : 35233 nanoseconds convertInputStreamToStringNioDirectBuffer() (Median 30/100) : 45869 nanoseconds convertInputStreamToStringNioDirectBufferWithCharset() (Median 30/100) : 45755 nanoseconds
メソッド名がObjective-C++よろしくエターナルフォースブリザード並に長くなっているけど気にしないことにする。
で、肝心の結果がallocateDirect()の場合遅くなっている。
これはまぁ書いている時に当然だろうと思っていたことで、ByteBuffer#array()
が使えないから
一旦同じサイズのbyte[]オブジェクトにコピーした後さらにByteArrayOutputStreamにwrite()しているため。
ByteArrayOutputStreamに直接write()できればいいが、そういったAPIはないのでどうしようもない。
InputStream→ByteBuffer(Cスタック)→byte[]オブジェクト→ByteArrayOutputStream→String
じゃ遅いに決まってる。
(もちろん実装クラスによっては間にもっと色々あるだろうけど)
結論とか
- やっぱNIO速い
- ベンチマークでは異常値に惑わされないことが重要
- ほかにも重要なことは山ほどあるけど自分もわかっていないので参考リンクを見てください
- 今回は単に「InputStream→Stringの変換」という事だったけど、InputStreamの実装クラスが決まればもっと速度最適化できるはず
- 例えばネットワーク/HDD/メモリから読み込む際に全て同じバッファーサイズを取るのはおかしい。動的に決めたり、上位から指定したり、色々やれることがあるはず
- あと関係ないけどEclipseってMarkdown Editorついてたのか…GitHub Fravoredじゃないので```でのコードブロックとか見れなかったけど