了解jvm中的循环性能

时间:2016-10-28 12:36:06

标签: java performance loops jmh

我正在玩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在提高性能方面所做的优化。

循环展开/流水线 是什么意思?

3 个答案:

答案 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);
}

Risc pipeline

IF =取指令,ID =指令解码,EX =执行,MEM =存储器访问,WB =寄存器回写

来自Oracle White paper

  

...标准编译器优化,可实现更快的循环执行。循环展开增加了循环体尺寸,同时减少了迭代次数。循环展开还可以提高其他优化的效率。

有关管道传输的更多信息: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:

  

软件流水线操作实际上与硬件流水线无关,是一种循环优化技术,可使迭代中的语句彼此独立。目标是删除依赖项,以便可以并行执行看似顺序的指令。

在此处查看更多信息: