HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

JDKのバージョンの違いによる文字列連結のパフォーマンスの差

もともとは、次のようなことを考えていた。

'+'演算子による文字列連結は、コンパイル時にStringBuffer/StringBuilderを使ったコードに置き換えられるんだよな。
だったら、

というケースに処理時間の差があるんじゃないだろうか。
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 に変わったときのパフォーマンスの劣化ぶりがすさまじい‥