我正在玩jmh
,在关于looping的部分中他们说过
你可能会注意到重复计数越大,越低 正在测量的操作的“感知”成本。到目前为止我们 以1/20 ns进行每次添加,远远超出硬件的实际范围 做。发生这种情况是因为 循环严重展开/流水线 ,并且 要测量的操作是 从循环中提升 。士气:不要 过度使用循环,依靠JMH来正确测量。
我自己尝试了
@Benchmark
@OperationsPerInvocation(1)
public int measurewrong_1() {
return reps(1);
}
@Benchmark
@OperationsPerInvocation(1000)
public int measurewrong_1000() {
return reps(1000);
}
并得到以下结果:
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
确实表明MyBenchmark.measurewrong_1000
明显快于MyBenchmark.measurewrong_1
。但我无法真正理解JVM在提高性能方面所做的优化。
循环展开/流水线 是什么意思?
答案 0 :(得分:8)
循环展开可以实现流水线操作。因此,可管道的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);
}
IF =取指令,ID =指令解码,EX =执行,MEM =存储器访问,WB =寄存器回写
...标准编译器优化,可实现更快的循环执行。循环展开增加了循环体尺寸,同时减少了迭代次数。循环展开还可以提高其他优化的效率。
有关管道传输的更多信息:Classic RISC pipeline
答案 1 :(得分:6)
循环展开是一种技巧,通过重复循环体来展平多个循环迭代 例如。在给定的例子中
for (int i = 0; i < reps; i++) {
s += (x + y);
}
可以由JIT编译器展开为类似
的东西 for (int i = 0; i < reps - 15; i += 16) {
s += (x + y);
s += (x + y);
// ... 16 times ...
s += (x + y);
}
然后可以进一步优化扩展循环体
for (int i = 0; i < reps - 15; i += 16) {
s += 16 * (x + y);
}
显然,计算16 * (x + y)
比计算(x + y)
16倍要快得多。
答案 2 :(得分:2)
循环流水线操作=软件流水线操作。
基本上,这是一种用于优化顺序循环迭代效率的技术,通过执行循环体中的一些指令 - in parrallel 。
当然,只有在满足某些条件时才能做到这一点,例如每次迭代都不依赖于其他条件等。
来自insidehpc.com:
软件流水线操作实际上与硬件流水线无关,是一种循环优化技术,可使迭代中的语句彼此独立。目标是删除依赖项,以便可以并行执行看似顺序的指令。
在此处查看更多信息: