JDKのバージョンの違いによる文字列連結のパフォーマンスの差
もともとは、次のようなことを考えていた。
'+'演算子による文字列連結は、コンパイル時にStringBuffer/StringBuilderを使ったコードに置き換えられるんだよな。
だったら、
- Java 2 SE 1.4.2 でコンパイルして Java 2 SE 1.4.2 で実行
- Java 2 SE 1.4.2 でコンパイルして Java 2 SE 5.0 で実行
- Java 2 SE 5.0 でコンパイルして Java 2 SE 5.0 で実行
というケースに処理時間の差があるんじゃないだろうか。
Java 2 SE 5.0 を使用するなら、Java 2 SE 1.4.2 でコンパイルしたものをそのまま実行するよりも、Java 2 SE 5.0 で再コンパイルしたほうがいいんじゃないだろうか。
しかし、実際に計測してみると、「あれっ!?」という結果が出てきたので、表題のような内容に変わった。
今回の計測に用いたのは、次の組み合わせ。
コンパイル | 実行 |
Sun Java 2 SE 1.4.2_19 | Sun Java 2 SE 1.4.2_19 |
Sun Java 2 SE 1.4.2_19 | Sun Java 2 SE 5.0 Update 22 |
Sun Java 2 SE 1.4.2_19 | Sun Java SE 6 Update 17 |
Sun Java 2 SE 1.4.2_19 | IBM Java 2 SE 5.0 SR10 |
Sun Java 2 SE 5.0 Update 22 | Sun Java 2 SE 5.0 Update 22 |
Sun Java 2 SE 5.0 Update 22 | Sun Java SE 6 Update 17 |
Sun Java 2 SE 5.0 Update 22 | IBM Java 2 SE 5.0 SR10 |
IBM Java 2 SE 5.0 SR10 | Sun Java 2 SE 5.0 Update 22 |
IBM Java 2 SE 5.0 SR10 | Sun Java SE 6 Update 17 |
IBM Java 2 SE 5.0 SR10 | IBM Java 2 SE 5.0 SR10 |
Sun Java SE 6 Update 17 | Sun Java SE 6 Update 17 |
これらすべての組み合わせについて、おおむね次のような処理をバッチで実行した。
SETLOCAL SET JAVA_HOME=[コンパイル用JDKのJAVA_HOME] SET PATH=%JAVA_HOME%\bin;%PATH% javac Main.java ENDLOCAL SETLOCAL SET JAVA_HOME=[実行用JDKのJAVA_HOME] SET PATH=%JAVA_HOME%\bin;%PATH% java Main "コンパイル用JDKのバージョン番号" ENDLOCAL
で、計測のためのコード。
import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; public class Main { private static NumberFormat nf = new DecimalFormat("0.000"); /** * @param args */ public static void main(String[] args) { System.err.println("java.version = " + System.getProperty("java.version")); System.err.println("java.vendor = " + System.getProperty("java.vendor")); System.err.println("java.runtime.version = " + System.getProperty("java.runtime.version")); System.err.println("os.name = " + System.getProperty("os.name")); int n = 10; /* * ["コンパイルに使用したJDKバージョン", "実行に使用したJDKバージョン", {n回分の処理時間}, 平均処理時間] */ List results = new ArrayList(n + 3); results.add(args.length > 0 ? args[0] : "unknown"); results.add(System.getProperty("java.version")); String s0 = "0000000000"; String s1 = "1111111111"; String s2 = "2222222222"; String s3 = "3333333333"; String s4 = "4444444444"; String s5 = "5555555555"; String s6 = "6666666666"; String s7 = "7777777777"; String s8 = "8888888888"; String s9 = "9999999999"; long totalTime = 0; for (int k = 0; k < n; ++k) { long start = System.currentTimeMillis(); try { String str = ""; for (int i = 0; i < 1000; ++i) { str += s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9; } } finally { long end = System.currentTimeMillis(); totalTime += (end - start); results.add(nf.format((end - start) / 1000.0)); System.err.println("time: " + nf.format((end - start) / 1000.0)); } } results.add(nf.format(1.0 * totalTime / n / 1000)); System.out.println(results); } }
あぁ、計測環境はいつものとおりWindows Vista Business on Let's Note CF-W5。
で、計測結果。
コンパイル | 実行 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 平均 |
Sun-1.4.2_19 | Sun-1.4.2_19 | 2.866 | 2.921 | 2.943 | 2.876 | 3.248 | 3.003 | 2.954 | 3.169 | 2.963 | 3.023 | 2.997 |
Sun-1.4.2_19 | Sun-1.5.0_22 | 5.425 | 5.474 | 5.457 | 5.534 | 5.430 | 5.392 | 5.367 | 5.371 | 5.424 | 5.515 | 5.439 |
Sun-1.5.0_22 | Sun-1.5.0_22 | 5.369 | 5.397 | 5.381 | 5.386 | 5.374 | 5.372 | 5.377 | 5.552 | 5.387 | 5.379 | 5.397 |
IBM-1.5.0 | Sun-1.5.0_22 | 5.468 | 5.487 | 5.453 | 5.464 | 5.481 | 5.488 | 5.461 | 5.473 | 5.473 | 5.599 | 5.485 |
Sun-1.4.2_19 | IBM-1.5.0 | 0.850 | 0.692 | 0.632 | 0.612 | 0.657 | 0.611 | 0.606 | 0.608 | 0.609 | 0.597 | 0.647 |
Sun-1.5.0_22 | IBM-1.5.0 | 0.780 | 0.680 | 0.632 | 0.611 | 0.611 | 0.650 | 0.699 | 0.602 | 0.606 | 0.606 | 0.648 |
IBM-1.5.0 | IBM-1.5.0 | 0.861 | 0.679 | 0.624 | 0.599 | 0.618 | 0.599 | 0.611 | 0.620 | 0.656 | 0.611 | 0.648 |
Sun-1.4.2_19 | Sun-1.6.0_17 | 1.171 | 1.181 | 1.219 | 1.170 | 1.173 | 1.193 | 1.202 | 1.187 | 1.167 | 1.174 | 1.184 |
Sun-1.5.0_22 | Sun-1.6.0_17 | 1.248 | 1.212 | 1.176 | 1.208 | 1.226 | 1.200 | 1.196 | 1.187 | 1.227 | 1.187 | 1.207 |
IBM-1.5.0 | Sun-1.6.0_17 | 1.181 | 1.245 | 1.176 | 1.184 | 1.195 | 1.243 | 1.182 | 1.195 | 1.183 | 1.237 | 1.202 |
Sun-1.6.0_17 | Sun-1.6.0_17 | 1.186 | 1.247 | 1.193 | 1.183 | 1.208 | 1.257 | 1.206 | 1.209 | 1.196 | 1.216 | 1.210 |
この表は、実行時に使用するJDKのバージョン順に並べてある。
コンパイルに用いたJDKのバージョンはあまり関係なく、むしろ実行時のJDKのバージョンによって処理時間がぜんぜん違うという結果。
特に、Java 2 SE 1.4.2 から Java 2 SE 5.0 に変わったときのパフォーマンスの劣化ぶりがすさまじい‥