为什么分配单个2D数组要比分配总大小和形状相同的多个1D数组的循环要花更长的时间?

问题描述 投票:22回答:2

我以为直接创建会更快,但是实际上,添加循环只需要一半的时间。发生了什么,放慢了很多?

这里是测试代码

@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 performance
2个回答
22
投票

在Java中,有一个单独的字节码指令用于分配多维数组-multianewarray

  • [multianewarray基准使用newArray字节码;
  • [multianewarray在循环中调用简单的newArray2

问题是HotSpot JVM 没有快速路径 *对于newarray字节码。该指令始终在VM运行时中执行。因此,分配未内联在编译后的代码中。

第一个基准测试必须付出在Java和VM Runtime上下文之间切换的性能损失。而且,VM运行时(用C ++编写)中的公共分配代码没有像JIT编译的代码中的内联分配那样优化,只是因为它是,即没有针对特定的对象类型或特定的对象进行优化。呼叫站点,它会执行其他运行时检查,等等。这里是使用multianewarray对两个基准进行概要分析的结果。我使用了JDK 11.0.4,但是对于JDK 8,图片看起来很相似。

async-profiler

newArray

在第一种情况下,在newArray2-VM运行时中的C ++代码内部花费了99%的时间。

在第二种情况下,图的大部分是绿色的,这意味着该程序主要在Java上下文中运行,实际上执行了为给定基准专门优化的JIT编译代码。

编辑

*

只是要澄清一下:在HotSpot中,OptoRuntime::multianewarray2_C在设计上没有很好地优化。在两个JIT编译器中正确实现这种复杂的操作相当昂贵,而这种优化的好处令人怀疑:在典型应用程序中,多维数组的分配很少会成为性能瓶颈。

6
投票
OptoRuntime::multianewarray2_C指令下multianewarray中的注释说:
© www.soinside.com 2019 - 2024. All rights reserved.