1. 测试循环代码耗时
加入我们希望测试一段代码, 循环 10w次, 耗时, 我们可以这么写
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Sample_11_Loops {
int x = 1;
int y = 2;
private int reps(int reps) {
int s = 0;
for (int i = 0; i < reps; i++) {
s += (x + y);
}
return s;
}
@Benchmark
public int measure100000() {
return reps(100_000);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Sample_11_Loops.class.getSimpleName())
.forks(1)
.jvmArgs("-server")
.build();
new Runner(opt).run();
}
}
测试结果:
Benchmark Mode Cnt Score Error Units
Sample_11_Loops.measure100000 avgt 1679.310 ns/op
2. @OperationsPerInvocation
但是我们需求有一点小改变
我们希望JMH帮我们计算出, 平均每次循环的耗时
那么我们就需要告诉JMH, 方法被调用一次, 会有多少次循环
@Benchmark
@OperationsPerInvocation(100_000)//告诉JMH这里重复了100000遍, 统计的时候调用一次要当做调用100000次来计算性能
public int measure100000() {
return reps(100_000);
}
- 这里我们使用
@OperationsPerInvocation(100_000)
注解, 告诉JMH一次顶10w次
统计结果
Benchmark Mode Cnt Score Error Units
Sample_11_Loops.measure100000 avgt 0.018 ns/op
这样JMH就会直接给我们把结果计算出来, 在原来的数值上除以10w
3. 谨慎使用循环
要注意, 循环写在测试代码中, JIT又会开始起作用, 可能会对测试结果会有极大的影响
例如这个例子:
@Benchmark
public int measure1() {
return reps(1);
}
@Benchmark
@OperationsPerInvocation(100_000)//告诉JMH这里重复了100000遍, 统计的时候调用一次要当做调用100000次来计算性能
public int measure100000() {
return reps(100_000);
}
根据我们的直觉, 这两个例子应该得出的结果是一致的
测试结果:
Benchmark Mode Cnt Score Error Units
Sample_11_Loops.measure1 avgt 1.471 ns/op
Sample_11_Loops.measure100000 avgt 0.017 ns/op
但实际上结果不一致,
按代码来看, 两个Benchmark算出的都是1次循环执行1次的平均耗时
但连续执行10w次的方法 性能远远好于执行1次的方法
这就是因为JIT对循环进行了优化, 导致了循环越多性能越强
所以我们在使用循环的时候, 要尽量与实际业务保持一致, 这样可以得到最真实的结果