雨谷の日和

過去19年で2,600を超えるアニメの第1話だけは見続けた僕のお勧めアニメがハズレなはずがない

何故Javaは遅いのか

実行時最適化を行わないJavaの遅さは、驚異的なものだった。
では何故、そんなにも遅いのだろうか。
もう一度、Javaが遅い理由として挙げられているものを振り返ってみよう。

  • 実行時のエラーチェックの厳密さ
  • VM(バーチャルマシン)という仕組み
  • 最適化の不十分さ
  • 言語仕様上の問題
  • ガベージコレクタの影響

※上記には、esper氏の指摘(GCの影響)も追加しました。有難うございます。
これらを検証するために、Javaの実行Profileを見てみよう。
Profileを取得するには、以下のようにしてJavaを実行すればよい。

java -Xprof -Xint Test >prof.txt

今回は最適化をせずに実行するので、「-Xint」オプションも同時に指定している。
結果は、以下である。

Flat profile of 153.22 secs (9788 total ticks): main

  Interpreted + native   Method
 70.8%  6790  +   140    sun.io.CharToByteDoubleByte.convert
  5.9%   581  +     0    sun.io.CharToByteMS932.convSingleByte
  4.1%   400  +     3    sun.nio.cs.StreamEncoder$ConverterSE.implWrite
  3.3%   182  +   142    sun.nio.cs.StreamEncoder.write
  2.8%   277  +     0    sun.nio.cs.StreamEncoder.write
  2.5%   243  +     1    sun.io.CharToByteConverter.convertAny
  2.4%   237  +     0    java.lang.String.getChars
  1.7%   164  +     0    java.io.PrintWriter.write
  0.9%    16  +    76    java.lang.System.arraycopy
  0.9%    92  +     0    Test.main
  0.9%    87  +     0    java.io.PrintWriter.write
  0.8%    75  +     0    java.io.PrintWriter.print
  0.4%    40  +     0    sun.nio.cs.StreamEncoder.ensureOpen
  0.3%    33  +     0    java.io.PrintWriter.ensureOpen
  0.3%     0  +    30    java.io.FileOutputStream.writeBytes
  0.3%    29  +     0    java.io.OutputStreamWriter.write
  0.2%    23  +     0    sun.io.CharToByteConverter.nextByteIndex
  0.2%    21  +     0    java.lang.String.length
  0.1%     0  +     9    java.lang.Throwable.fillInStackTrace
  0.0%     0  +     1    java.nio.charset.Charset$1.
  0.0%     1  +     0    java.io.FileOutputStream.write
  0.0%     1  +     0    java.lang.ref.SoftReference.get
  0.0%     1  +     0    java.io.ByteArrayInputStream.read
 99.1%  9293  +   402    Total interpreted

  Thread-local ticks:
  0.0%     1             Blocked (of total)
  0.0%     1             Class loader
  0.6%    59             Interpreter
  0.3%    27             Unknown: running frame
  0.1%     5             Unknown: thread_state


Global summary of 153.22 seconds:
100.0%  9807             Received ticks
  0.1%    10             Received GC ticks
  0.1%     7             Other VM operations
  0.0%     1             Class loader
  0.6%    59             Interpreter
  0.3%    32             Unknown code

これを見て分かることは、sun.io.CharToByteDoubleByte.convertの処理にほとんど(70%)の時間を費やしているということである。
「Received GC ticks」の処理全体に占める割合が僅か0.1%であることから、この処理の場合にはGCの影響を無視して良さそうだ。
(もちろん、GCの影響の大きな処理も存在するだろう。それは別途検証する必要がある)
エラーチェックについては、このリストを見るだけでは良く分からない。どなたか分かる方に解説をお願いしたいところだ。
結局、この実行速度の遅さはJavaVM上で動いているということに大きく依存しているのではないだろうか。
次は、実行時最適化の際のProfileと比較して、それを検証してみたい。