Java 验证simple for/lambda比较的JMH测量值

Java 验证simple for/lambda比较的JMH测量值,java,java-stream,benchmarking,jmh,Java,Java Stream,Benchmarking,Jmh,我想对simple for循环和等效流实现进行一些性能测量和比较。我相信在这种情况下,streams会比等效的非streams代码慢一些,但我想确定我度量的是正确的东西 我把我的整个jmh班都包括在这里 import java.util.ArrayList; import java.util.List; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import o

我想对simple for循环和等效流实现进行一些性能测量和比较。我相信在这种情况下,streams会比等效的非streams代码慢一些,但我想确定我度量的是正确的东西

我把我的整个jmh班都包括在这里

import java.util.ArrayList;
import java.util.List;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
public class MyBenchmark {
    List<String>    shortLengthListConstantSize     = null;
    List<String>    mediumLengthListConstantSize    = null;
    List<String>    longerLengthListConstantSize    = null;
    List<String>    longLengthListConstantSize      = null;

    @Setup
    public void setup() {
        shortLengthListConstantSize     = populateList(2);
        mediumLengthListConstantSize    = populateList(12);
        longerLengthListConstantSize    = populateList(300);
        longLengthListConstantSize      = populateList(300000);
    }

    private List<String> populateList(int size) {
        List<String> list   = new ArrayList<>();
        for (int ctr = 0; ctr < size; ++ ctr) {
            list.add("xxx");
        }
        return list;
    }

    @Benchmark
    public long shortLengthConstantSizeFor() {
        long count   = 0;
        for (String val : shortLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long shortLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        shortLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long shortLengthConstantSizeLambda() {
        return shortLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long shortLengthConstantSizeLambdaParallel() {
        return shortLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long mediumLengthConstantSizeFor() {
        long count   = 0;
        for (String val : mediumLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long mediumLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        mediumLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long mediumLengthConstantSizeLambda() {
        return mediumLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long mediumLengthConstantSizeLambdaParallel() {
        return mediumLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longerLengthConstantSizeFor() {
        long count   = 0;
        for (String val : longerLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long longerLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        longerLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long longerLengthConstantSizeLambda() {
        return longerLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longerLengthConstantSizeLambdaParallel() {
        return longerLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longLengthConstantSizeFor() {
        long count   = 0;
        for (String val : longLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long longLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        longLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long longLengthConstantSizeLambda() {
        return longLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longLengthConstantSizeLambdaParallel() {
        return longLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    public static class IntHolder {
        public int value    = 0;
    }
}
在前面的一个问题中,我证实了这些基准似乎“功能等同”(只是寻找额外的眼睛)。这些数字是否与这些基准的独立运行一致

对于JMH输出,我一直不确定的另一件事是确定吞吐量数字到底代表什么。例如,“Cnt”列中的“200”代表什么?吞吐量单位是“每秒操作数”,那么“操作数”到底代表什么,是对基准方法的一次调用的执行?例如,在最后一行中,这表示每秒执行474k次基准测试方法

更新

我注意到,当我将“for”与“lambda”进行比较时,从“short”列表开始,到更长的列表,它们之间的比率相当大,但会下降,直到“long”列表,其中的比率甚至比“short”列表更大(14%、29%、78%和11%)。我觉得这很奇怪。我本以为流开销的比率会随着实际业务逻辑中的工作的增加而降低。有人对此有什么想法吗

例如,“Cnt”列中的“200”代表什么

cnt
列是迭代次数-即重复测试的次数。可以使用以下注释控制该值:

  • 对于实际测量:
    @Measurement(迭代次数=10,时间=50,时间单位=timeUnit.ms)
  • 对于预热阶段:
    @warmup(迭代次数=10,时间=1,时间单位=timeUnit.SECONDS)
这里的
iterations
cnt
<代码>时间是一次迭代所需的持续时间,
时间单位
是时间值的度量单位

吞吐量单位为“每秒操作数”

您可以通过多种方式控制输出。例如,您可以使用
@OutputTimeUnit(TimeUnit.XXXX)
更改时间的测量单位,这样您就可以得到ops/us,ops/ms

您还可以更改
模式
:您可以测量“平均时间”、“采样时间”等,而不是测量操作/时间。您可以通过
@BenchmarkMode({mode.AverageTime})
注释来控制这一点

那么“操作”到底代表了什么,就是对基准方法的一次调用的执行

假设一次迭代的时间为1秒,每秒1000次。这意味着benchamrk方法已经执行了1000次

换句话说,一个操作就是基准方法的一次执行,除非您有
@operationspernitude(XXX)
注释,这意味着方法的教学调用将计为XXX操作

在所有迭代中计算误差


还有一个提示:您可以执行参数化基准测试,而不是对每个可能的大小进行硬编码:

@Param({"3", "12", "300", "3000"})
private int length;
然后,您可以在设置中使用该参数:

 @Setup(Level.Iteration)
 public void setUp(){
     populateList(length)
 }

提示:JMH支持不需要为不同输入重复代码的设置。已确认。我将把它合并到下一个版本中。
 @Setup(Level.Iteration)
 public void setUp(){
     populateList(length)
 }