負け犬プログラマーの歩み

負け組だった人間が今一度PGとして人生の飛躍を模索するも加齢と共に閉ざされる未来に直面しているブログ。

JavaとC#のパフォーマンスを比較する。

実装やコンパイラの性能次第で幾らでも変わると思うのだが、プログラミング言語の速度については、大体以下のようにグループ分けして考えている。

f:id:Cedille:20150117230403p:plain

同グループ内であれば速度は似たり寄ったりなので「どっちが速いのか」という思索に至ることはなく、例えば「JavaC#のどちらが速いのか?」という点についてはこれまであまり意識することはなかった。

ところが、今日こんな記事を見た。

JavaとC#の「int」の比較 - Qiita

有り難いことに、昔書いたエントリーが紹介されており、面倒臭かったから探しもせず、計測もしなかったJavaのプリミティブ型とC#のデータ型のパフォーマンスを調べて頂いており、5万回ずつ単純な数値型を増減する処理を実行しており、最後にはこういう結果が出ていた。

Java : 2783ms.
C# : 15236ms.
という結果が出ました!

これは少し驚きだ。

というのもC#の方が6-7倍も遅いのだ。俺の言語速度のヒエラルキー図が完全に崩れてしまう。とりわけ上の記事で実行されていたC#コードは、構造体からオブジェクトへのボックス化が発生することはないため、C#が特段不利になるようなものでもないと解される。

だが、いざ実行されたコードを試してみると、その理由が理解できた。というのも、上記の結果はデバッグビルトのものであり、恐らくVisual Studio上で実行したものだと解される。だからC#にものすごく不利な条件で測定されたものと解される。どうして不利かというと、要はJITコンパイルで最適化されていないのである。

上記のコードをデバッグとリリースモードでC#コンパイラがILにしたものが以下である。

Debug

 ILを見る

Release

 ILを見る

わざわざ長いコードを貼って恐縮ではあるが、生成されるILにあまり大きな違いはない。}の部分でもブレークポイントを設置できるようにILでnop(No Operation)命令が埋め込まれていたりするが、これだけでは流石に大きな違いは出てこない。一番の違いは、この後更にJITコンパイラがネイティブコードに変換するときに、メソッドのインライン展開やレジスタ割り当てなどの最適化が実施される。特にこのコード例の場合、レジスタ割り当てがかなり効きそう。

ところで、比較するならJavaについても調べないといけないが、C#みたいにIDEで実行したかどうかで違いがあるのかどうか分からんが、とりあえずコマンドラインでjavacして実行してみると・・・「3ms」とか「4ms」とか異様に速いんだけどッ!? …ってコードをよく見たら、恐らくVMが最終的にansというローカル変数が使われないからって最初っから計算していない可能性が高い。という訳でコードを以下のように修正してもう一度実行したらちゃんとした値は出た。この辺りはJavaVMの方が頭が良いとは感じた。

public class TestOfInt {

    /**
     * @param args
     */
    public static void main(String[] args) {
        int ans = 0;

        long start = System.currentTimeMillis();

        for(int j = 0; j < 50000; j++) {
            for(int i = 1; i <= 50000; i++) {
                ans += i;
            }

            for(int i = 1; i <= 50000; i++) {
                ans -= i;
            }
        }

        long end = System.currentTimeMillis();

        System.out.println(end - start + "ms. " + ans); // ここを修正
    }
}


最終的な結果はこうなった。なおCore™ i7-3770KとDDR3 16GB。

C#(リリース) 1411ms
Java     1508ms
C#(デバッグ) 14051ms