バックグラウンド
明示的なJavaダウンキャストのパフォーマンスを測定するためのコード(下部に投稿)を実行していて、少し異常な感じに遭遇しました...またはおそらく2つの異常です。
私はすでにJavaキャストのオーバーヘッドに関するこのスレッドを見てきましたが、この特定の現象ではなく、一般的なキャストについてのみ話しているように見えました。このスレッドは同様のトピックをカバーしており、時期尚早の最適化についてのアドバイスは本当に必要ありません。パフォーマンスを最大化するためにアプリケーションの一部を調整しているので、これは論理的なステップです。
テスト
基本的に、ダウンキャストのパフォーマンスと、sであるが、sとして入力された.toString()
オブジェクトのメソッドをテストしたかったのです。そこで、同等のコンテンツでとを作成し、3つのループを実行して、タイミングを合わせました。String
Object
String a
Object b
- ループ1は
((String) b).toLowerCase()
; - ループ2は
b.toString().toLowerCase()
; - ループ3は
a.toLowerCase()
です。
試験結果
(ミリ秒単位の測定値。)
iters | Test Round | Loop 1 | Loop 2 | Loop 3
-----------|--------------|----------|----------|----------
50,000,000 | 1 | 3367 | 3166 | 3186
Test A | 2 | 3543 | 3158 | 3156
| 3 | 3365 | 3155 | 3169
-----------|--------------|----------|----------|----------
5,000,000 | 1 | 373 | 348 | 369
Test B | 2 | 373 | 348 | 370
| 3 | 399 | 334 | 371
-----------|--------------|----------|----------|----------
500,000 | 1 | 66 | 36 | 33
Test C | 2 | 71 | 36 | 41
| 3 | 66 | 35 | 34
-----------|--------------|----------|----------|----------
50,000 | 1 | 27 | 5 | 5
Test D | 2 | 27 | 6 | 5
| 3 | 26 | 5 | 5
-----------|--------------|----------|----------|----------
テストに使用されるコード
long t, iters = ...;
String a = "String", c;
Object b = "String";
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = ((String) b).toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = b.toString().toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
t = System.currentTimeMillis();
for (int i = 0; i < iters; i++) {
c = a.toLowerCase();
}
System.out.println(System.currentTimeMillis() - t);
最後に、質問
私が最も魅力的だと思うのは、ループ2(.toString()
)が3つのうち(特にテストBで)最高のパフォーマンスを発揮しているように見えたことです。これは直感的には意味がありません。なぜ、すでにオブジェクトを持っているよりも呼び出しが速いのでしょうか?.toString()
String
私を悩ませているもう一つのことは、それがスケーリングしないということです。テストAとDを比較すると、互いに比較すると9倍ずれています(27 * 1000 = 27000、3000ではありません)。反復回数が増えるにつれて、なぜこの大きな不一致があるのでしょうか。
これらの2つの異常が真実であることが証明された理由について誰かが説明を提供できますか?
(奇妙な)現実
更新:Bruno Reisのソリューションのアドバイスを受けて、コンパイラ出力を使用してベンチマークを再度実行しました。最初のループは初期化のものでいっぱいだったので、これを行うために「ジャンク」ループを入れました。それが行われると、結果は予想に近づきました。
これは、5,000,000回の反復を使用したコンソールからの完全な出力です(私がコメントしました)。
50 1 java.lang.String::toLowerCase (472 bytes)
50 2 java.lang.CharacterData::of (120 bytes)
53 3 java.lang.CharacterDataLatin1::getProperties (11 bytes)
53 4 java.lang.Character::toLowerCase (9 bytes)
54 5 java.lang.CharacterDataLatin1::toLowerCase (39 bytes)
67 6 n java.lang.System::arraycopy (0 bytes) (static)
68 7 java.lang.Math::min (11 bytes)
68 8 java.util.Arrays::copyOfRange (63 bytes)
69 9 java.lang.String::toLowerCase (8 bytes)
69 10 java.util.Locale::getDefault (13 bytes)
70 1 % Main::main @ 14 (175 bytes)
[GC 49088K->360K(188032K), 0.0007670 secs]
[GC 49448K->360K(188032K), 0.0024814 secs]
[GC 49448K->328K(188032K), 0.0005422 secs]
[GC 49416K->328K(237120K), 0.0007519 secs]
[GC 98504K->352K(237120K), 0.0122388 secs]
[GC 98528K->352K(327552K), 0.0005734 secs]
595 1 % Main::main @ -2 (175 bytes) made not entrant
548 /****** Junk Loop ******/
597 2 % Main::main @ 61 (175 bytes)
[GC 196704K->356K(327552K), 0.0008460 secs]
[GC 196708K->388K(523968K), 0.0005100 secs]
343 /****** Loop 1 ******/
939 2 % Main::main @ -2 (175 bytes) made not entrant
940 11 java.lang.String::toString (2 bytes)
940 3 % Main::main @ 103 (175 bytes)
[GC 393092K->356K(523968K), 0.0036496 secs]
377 /****** Loop 2 ******/
1316 3 % Main::main @ -2 (175 bytes) made not entrant
1317 4 % Main::main @ 145 (175 bytes)
[GC 393060K->332K(759680K), 0.0008326 secs]
320 /****** Loop 3 ******/