私はそれが直接作成するために迅速だろうと思ったが、実際には、ループを追加するだけで半分の時間がかかります。そんなに遅くなること何が起こったのか?
ここでは、テストコードがあります
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class Test_newArray {
private static int num = 10000;
private static int length = 10;
@Benchmark
public static int[][] newArray() {
return new int[num][length];
}
@Benchmark
public static int[][] newArray2() {
int[][] temps = new int[num][];
for (int i = 0; i < temps.length; i++) {
temps[i] = new int[length];
}
return temps;
}
}
テスト結果は次の通りです。
Benchmark Mode Cnt Score Error Units
Test_newArray.newArray avgt 25 289.254 ± 4.982 us/op
Test_newArray.newArray2 avgt 25 114.364 ± 1.446 us/op
次のようにテスト環境があります
JMHバージョン:1.21
VMのバージョン:JDK 1.8.0_212、OpenJDKの64ビットサーバーVM、25.212-B04
Javaで多次元配列を割り当てるための別のバイトコード命令があります- multianewarray
。
newArray
ベンチマークの用途は、multianewarray
バイトコード。newArray2
シンプルを呼び出すnewarray
ループで。
問題は、HotSpotのJVMがあることである何のファストパスがありません*のためのmultianewarray
バイトコードを。この命令は常にVMランタイムで実行されます。したがって、割り当ては、コンパイルされたコードにインライン化されていません。
最初のベンチマークでは、Java VMおよびランタイムコンテキストの切り替えのパフォーマンスペナルティを支払わなければなりません。また、VMのランタイム(C ++で書かれて)で一般的な割り当てコードは次のようにそれがあるという理由だけで、JITコンパイルされたコードでインライン配分として最適化されていない一般的な、すなわち、それは、特定のオブジェクト・タイプまたは特定の呼び出しサイト用に最適化されていません等、追加のランタイムチェックを行います
ここで、両方のベンチマークをプロファイリングの結果である非同期プロファイラは。私は、JDK 11.0.4を使用しますが、JDK 8のために絵が似ています。
最初のケースでは、99%の時間は、内部に費やされているOptoRuntime::multianewarray2_C
VMの実行時にC ++コード- 。
後者の場合、グラフのほとんどは、プログラムが実際に与えられたベンチマークのために特別に最適化されたJITコンパイルされたコードを実行し、Javaのコンテキストで主に実行されることを意味し、緑色です。
EDIT
*ただ、明確にする:ホットスポットにmultianewarray
設計することにより、非常にうまく最適化されていません。このような最適化のメリットは疑問だろうしながら、適切に両方のJITコンパイラでは、このような複雑な動作を実現するためにかなり高価です:多次元配列の割り当てはほとんど一般的なアプリケーションにおけるパフォーマンスのボトルネックではありません。