我以为直接创建会更快,但是实际上,添加循环只需要一半的时间。发生了什么,放慢了很多?
这里是测试代码
@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
。
multianewarray
基准使用newArray
字节码;multianewarray
在循环中调用简单的newArray2
。问题是HotSpot JVM 没有快速路径 *对于newarray
字节码。该指令始终在VM运行时中执行。因此,分配未内联在编译后的代码中。
第一个基准测试必须付出在Java和VM Runtime上下文之间切换的性能损失。而且,VM运行时(用C ++编写)中的公共分配代码没有像JIT编译的代码中的内联分配那样优化,只是因为它是multianewarray
对两个基准进行概要分析的结果。我使用了JDK 11.0.4,但是对于JDK 8,图片看起来很相似。
在第一种情况下,在-VM运行时中的C ++代码内部花费了99%的时间。
在第二种情况下,图的大部分是绿色的,这意味着该程序主要在Java上下文中运行,实际上执行了为给定基准专门优化的JIT编译代码。
编辑
*
只是要澄清一下:在HotSpot中,OptoRuntime::multianewarray2_C
在设计上没有很好地优化。在两个JIT编译器中正确实现这种复杂的操作相当昂贵,而这种优化的好处令人怀疑:在典型应用程序中,多维数组的分配很少会成为性能瓶颈。OptoRuntime::multianewarray2_C
指令下multianewarray
中的注释说: