なぜ同じ合計サイズおよび形状の複数の1次元アレイを割り当てるループよりも長い単一の2Dアレイテイクを割り当てていますか?

user10339780:

私はそれが直接作成するために迅速だろうと思ったが、実際には、ループを追加するだけで半分の時間がかかります。そんなに遅くなること何が起こったのか?

ここでは、テストコードがあります

@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

apangin:

Javaで多次元配列を割り当てるための別のバイトコード命令があります- multianewarray

  • newArrayベンチマークの用途は、multianewarrayバイトコード。
  • newArray2シンプルを呼び出すnewarrayループで。

問題は、HotSpotのJVMがあることである何のファストパスがありません*のためのmultianewarrayバイトコードを。この命令は常にVMランタイムで実行されます。したがって、割り当ては、コンパイルされたコードにインライン化されていません。

最初のベンチマークでは、Java VMおよびランタイムコンテキストの切り替えのパフォーマンスペナルティを支払わなければなりません。また、VMのランタイム(C ++で書かれて)で一般的な割り当てコードは次のようにそれがあるという理由だけで、JITコンパイルされたコードでインライン配分として最適化されていない一般的な、すなわち、それは、特定のオブジェクト・タイプまたは特定の呼び出しサイト用に最適化されていません等、追加のランタイムチェックを行います

ここで、両方のベンチマークをプロファイリングの結果である非同期プロファイラは私は、JDK 11.0.4を使用しますが、JDK 8のために絵が似ています。

NEWARRAY

newArray2

最初のケースでは、99%の時間は、内部に費やされているOptoRuntime::multianewarray2_CVMの実行時にC ++コード- 。

後者の場合、グラフのほとんどは、プログラムが実際に与えられたベンチマークのために特別に最適化されたJITコンパイルされたコードを実行し、Javaのコンテキストで主に実行されることを意味し、緑色です。

EDIT

*ただ、明確にする:ホットスポットにmultianewarray設計することにより、非常にうまく最適化されていません。このような最適化のメリットは疑問だろうしながら、適切に両方のJITコンパイラでは、このような複雑な動作を実現するためにかなり高価です:多次元配列の割り当てはほとんど一般的なアプリケーションにおけるパフォーマンスのボトルネックではありません。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=311836&siteId=1