我认为直接创建起来会更快,但是实际上,添加循环只需要一半的时间。发生了什么,放慢了这么多?
这是测试代码
@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;
}
}
Run Code Online (Sandbox Code Playgroud)
测试结果如下。
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
Run Code Online (Sandbox Code Playgroud)
测试环境如下
JMH版本:1.21
VM版本:JDK 1.8.0_212,OpenJDK 64位服务器VM,25.212-b04
apa*_*gin 81
在Java中,有一个单独的字节码指令用于分配多维数组- multianewarray。
newArray基准测试使用multianewarray字节码;newArray2newarray在循环中调用simple 。问题是,热点JVM 有没有快速路径*为multianewarray字节码。该指令始终在VM运行时中执行。因此,分配未内联在编译的代码中。
第一个基准测试必须付出在Java和VM Runtime上下文之间切换的性能损失。另外,VM运行时(用C ++编写)中的公共分配代码没有像JIT编译的代码中的内联分配那样优化,只是因为它是通用的,即没有针对特定的对象类型或特定的调用站点进行优化,执行其他运行时检查等。
这是使用async-profiler对两个基准进行概要分析的结果。我使用的是JDK 11.0.4,但是对于JDK 8来说,图片看起来很相似。
在第一种情况下,内部花费了99%的时间OptoRuntime::multianewarray2_C-VM运行时中的C ++代码。
在第二种情况下,大多数图形是绿色的,这意味着该程序主要在Java上下文中运行,实际上是执行针对给定基准专门优化的JIT编译代码。
编辑
*需要澄清的是:HotSpot multianewarray在设计上没有很好地优化。在两个JIT编译器中正确实现这样的复杂操作相当昂贵,而这种优化的好处令人怀疑:在典型应用程序中,多维数组的分配很少会成为性能瓶颈。
Ole*_*hov 17
在A记的Oracle文档下multianewarray指示说:
创建单个维度的数组时,使用
newarray或anewarray(§newarray,§anewarray)可能会更有效。
进一步:
newArray基准测试使用multianewarray字节码指令。
newArray2基准测试使用anewarray字节码指令。
这就是与众不同的地方。让我们看看使用perfLinux Profiler 获得的统计信息。
对于newArray基准,内联后最热门的方法是:
....[Hottest Methods (after inlining)]..............................................................
22.58% libjvm.so MemAllocator::allocate
14.80% libjvm.so ObjArrayAllocator::initialize
12.92% libjvm.so TypeArrayKlass::multi_allocate
10.98% libjvm.so AccessInternal::PostRuntimeDispatch<G1BarrierSet::AccessBarrier<2670710ul, G1BarrierSet>, (AccessInternal::BarrierType)1, 2670710ul>::oop_access_barrier
7.38% libjvm.so ObjArrayKlass::multi_allocate
6.02% libjvm.so MemAllocator::Allocation::notify_allocation_jvmti_sampler
5.84% ld-2.27.so __tls_get_addr
5.66% libjvm.so CollectedHeap::array_allocate
5.39% libjvm.so Klass::check_array_allocation_length
4.76% libc-2.27.so __memset_avx2_unaligned_erms
0.75% libc-2.27.so __memset_avx2_erms
0.38% libjvm.so __tls_get_addr@plt
0.17% libjvm.so memset@plt
0.10% libjvm.so G1ParScanThreadState::copy_to_survivor_space
0.10% [kernel.kallsyms] update_blocked_averages
0.06% [kernel.kallsyms] native_write_msr
0.05% libjvm.so G1ParScanThreadState::trim_queue
0.05% libjvm.so Monitor::lock_without_safepoint_check
0.05% libjvm.so G1FreeCollectionSetTask::G1SerialFreeCollectionSetClosure::do_heap_region
0.05% libjvm.so OtherRegionsTable::occupied
1.92% <...other 288 warm methods...>
....[Distribution by Source]....
87.61% libjvm.so
5.84% ld-2.27.so
5.56% libc-2.27.so
0.92% [kernel.kallsyms]
0.03% perf-27943.map
0.03% [vdso]
0.01% libpthread-2.27.so
................................
100.00% <totals>
Run Code Online (Sandbox Code Playgroud)
对于newArray2:
....[Hottest Methods (after inlining)]..............................................................
93.45% perf-28023.map [unknown]
0.26% libjvm.so G1ParScanThreadState::copy_to_survivor_space
0.22% [kernel.kallsyms] update_blocked_averages
0.19% libjvm.so OtherRegionsTable::is_empty
0.17% libc-2.27.so __memset_avx2_erms
0.16% libc-2.27.so __memset_avx2_unaligned_erms
0.14% libjvm.so OptoRuntime::new_array_C
0.12% libjvm.so G1ParScanThreadState::trim_queue
0.11% libjvm.so G1FreeCollectionSetTask::G1SerialFreeCollectionSetClosure::do_heap_region
0.11% libjvm.so MemAllocator::allocate_inside_tlab_slow
0.11% libjvm.so ObjArrayAllocator::initialize
0.10% libjvm.so OtherRegionsTable::occupied
0.10% libjvm.so MemAllocator::allocate
0.10% libjvm.so Monitor::lock_without_safepoint_check
0.10% [kernel.kallsyms] rt2800pci_rxdone_tasklet
0.09% libjvm.so G1Allocator::unsafe_max_tlab_alloc
0.08% libjvm.so ThreadLocalAllocBuffer::fill
0.08% ld-2.27.so __tls_get_addr
0.07% libjvm.so G1CollectedHeap::allocate_new_tlab
0.07% libjvm.so TypeArrayKlass::allocate_common
4.15% <...other 411 warm methods...>
....[Distribution by Source]....
93.45% perf-28023.map
4.31% libjvm.so
1.64% [kernel.kallsyms]
0.42% libc-2.27.so
0.08% ld-2.27.so
0.06% [vdso]
0.04% libpthread-2.27.so
................................
100.00% <totals>
Run Code Online (Sandbox Code Playgroud)
如我们所见,对于最慢newArray的时间,大部分时间都花在了jvm代码中(总计87.61%):
22.58% libjvm.so MemAllocator::allocate
14.80% libjvm.so ObjArrayAllocator::initialize
12.92% libjvm.so TypeArrayKlass::multi_allocate
7.38% libjvm.so ObjArrayKlass::multi_allocate
...
Run Code Online (Sandbox Code Playgroud)
尽管newArray2使用OptoRuntime::new_array_C,则花费更少的时间为数组分配内存。在jvm代码中花费的总时间仅为4.31%。
使用perfnorm探查器获得的奖金统计信息:
Benchmark Mode Cnt Score Error Units
newArray avgt 4 448.018 ± 80.029 us/op
newArray:CPI avgt 0.359 #/op
newArray:L1-dcache-load-misses avgt 10399.712 #/op
newArray:L1-dcache-loads avgt 1032985.924 #/op
newArray:L1-dcache-stores avgt 590756.905 #/op
newArray:cycles avgt 1132753.204 #/op
newArray:instructions avgt 3159465.006 #/op
Benchmark Mode Cnt Score Error Units
newArray2 avgt 4 125.531 ± 50.749 us/op
newArray2:CPI avgt 0.532 #/op
newArray2:L1-dcache-load-misses avgt 10345.720 #/op
newArray2:L1-dcache-loads avgt 85185.726 #/op
newArray2:L1-dcache-stores avgt 103096.223 #/op
newArray2:cycles avgt 346651.432 #/op
newArray2:instructions avgt 652155.439 #/op
Run Code Online (Sandbox Code Playgroud)
注意周期数和指令数的差异。
环境:
Ubuntu 18.04.3 LTS
java version "12.0.2" 2019-07-16
Java(TM) SE Runtime Environment (build 12.0.2+10)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5014 次 |
| 最近记录: |