St.*_*rio 12 java performance loops jmh
我正在玩,jmh在关于循环的部分他们说
您可能会注意到重复次数越多,所测量操作的"感知"成本就越低.到目前为止,我们每增加1/20 ns,远远超出硬件实际可行的范围.发生这种情况是因为循环被大量展开/流水线化,并且要从循环中提升要测量的操作.士气:不要过度使用循环,依靠JMH来获得正确的测量.
我亲自尝试过
@Benchmark
@OperationsPerInvocation(1)
public int measurewrong_1() {
return reps(1);
}
@Benchmark
@OperationsPerInvocation(1000)
public int measurewrong_1000() {
return reps(1000);
}
Run Code Online (Sandbox Code Playgroud)
得到以下结果:
Benchmark Mode Cnt Score Error Units
MyBenchmark.measurewrong_1 avgt 15 2.425 ± 0.137 ns/op
MyBenchmark.measurewrong_1000 avgt 15 0.036 ± 0.001 ns/op
Run Code Online (Sandbox Code Playgroud)
它确实表明它MyBenchmark.measurewrong_1000比它快得多MyBenchmark.measurewrong_1.但我无法真正理解JVM在提高性能方面所做的优化.
他们的意思是循环展开/流水线?
循环展开使流水线成为可能.因此,可管道的CPU(例如RISC)可以并行执行展开的代码.
因此,如果您的CPU能够并行执行5个管道,那么您的循环将以以下方式展开:
// pseudo code
int pipelines = 5;
for(int i = 0; i < length; i += pipelines){
s += (x + y);
s += (x + y);
s += (x + y);
s += (x + y);
s += (x + y);
}
Run Code Online (Sandbox Code Playgroud)
IF =指令获取,ID =指令解码,EX =执行,MEM =存储器访问,WB =寄存器写回
来自Oracle白皮书:
...标准编译器优化,可实现更快的循环执行.循环展开增加了循环体尺寸,同时减少了迭代次数.循环展开还可以提高其他优化的效率.
有关管道传输的更多信息:经典RISC管道
循环展开是一种技术,通过重复循环体来展平多个循环迭代.
例如,在给定的例子中
for (int i = 0; i < reps; i++) {
s += (x + y);
}
Run Code Online (Sandbox Code Playgroud)
可以通过JIT编译器将其展开为类似的东西
for (int i = 0; i < reps - 15; i += 16) {
s += (x + y);
s += (x + y);
// ... 16 times ...
s += (x + y);
}
Run Code Online (Sandbox Code Playgroud)
然后可以进一步优化扩展的循环体
for (int i = 0; i < reps - 15; i += 16) {
s += 16 * (x + y);
}
Run Code Online (Sandbox Code Playgroud)
显然,计算16 * (x + y)速度比计算速度快(x + y)16倍.