我想知道为什么使用Blackhole.consumeCPU()
是明智的。
关于Blackhole.consumeCPU()
的Google搜索
有时候,当我们在多个线程上运行一个基准测试时,我们也希望在运行代码时消耗一些CPU周期来模拟CPU业务。这不可能是一个Thread.sleep,因为我们真的想消耗CPU。Blackhole.consumeCPU(long)使我们能够做到这一点。
我的示例代码:
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class StringConcatAvgBenchmark {
StringBuilder stringBuilder1;
StringBuilder stringBuilder2;
StringBuffer stringBuffer1;
StringBuffer stringBuffer2;
String string1;
String string2;
/*
* re-initializing the value after every iteration
*/
@Setup(Level.Iteration)
public void init() {
stringBuilder1 = new StringBuilder("foo");
stringBuilder2 = new StringBuilder("bar");
stringBuffer1 = new StringBuffer("foo");
stringBuffer2 = new StringBuffer("bar");
string1 = new String("foo");
string2 = new String("bar");
}
@Benchmark
@Warmup(iterations = 10)
@Measurement(iterations = 100)
@BenchmarkMode(Mode.AverageTime)
public StringBuilder stringBuilder() {
// operation is very thin and so consuming some CPU
Blackhole.consumeCPU(100);
return stringBuilder1.append(stringBuilder2);
// to avoid dead code optimization returning the value
}
@Benchmark
@Warmup(iterations = 10)
@Measurement(iterations = 100)
@BenchmarkMode(Mode.AverageTime)
public StringBuffer stringBuffer() {
Blackhole.consumeCPU(100);
// to avoid dead code optimization returning the value
return stringBuffer1.append(stringBuffer2);
}
@Benchmark
@Warmup(iterations = 10)
@Measurement(iterations = 100)
@BenchmarkMode(Mode.AverageTime)
public String stringPlus() {
Blackhole.consumeCPU(100);
return string1 + string2;
}
@Benchmark
@Warmup(iterations = 10)
@Measurement(iterations = 100)
@BenchmarkMode(Mode.AverageTime)
public String stringConcat() {
Blackhole.consumeCPU(100);
// to avoid dead code optimization returning the value
return string1.concat(string2);
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(StringConcatAvgBenchmark.class.getSimpleName())
.threads(1).forks(1).shouldFailOnError(true).shouldDoGC(true)
.jvmArgs("-server").build();
new Runner(options).run();
}
}
为什么blackhole.consumeCPU(100)
的基准测试结果更好?
更新:
blackhole.consumeCPU(100)
的输出:
Benchmark Mode Cnt Score Error Units
StringBenchmark.stringBuffer avgt 10 398,843 ± 38,666 ns/op
StringBenchmark.stringBuilder avgt 10 387,543 ± 40,087 ns/op
StringBenchmark.stringConcat avgt 10 410,256 ± 33,194 ns/op
StringBenchmark.stringPlus avgt 10 386,472 ± 21,704 ns/op
输出不带blackhole.consumeCPU(100)
:
Benchmark Mode Cnt Score Error Units
StringBenchmark.stringBuffer avgt 10 51,225 ± 19,254 ns/op
StringBenchmark.stringBuilder avgt 10 49,548 ± 4,126 ns/op
StringBenchmark.stringConcat avgt 10 50,373 ± 1,408 ns/op
StringBenchmark.stringPlus avgt 10 87,942 ± 1,701 ns/op
我想我现在知道他们为什么使用这个了,因为基准测试太快了,没有一些延迟。
使用blackhole.consumeCPU(100)
,您可以更好地测量每个基准,并获得更有意义的结果。是这样吗?
1条答案
按热度按时间hlswsv351#
增加人为延迟通常不会改善基准。
但是,在某些情况下,您正在测量的操作正在争夺一些资源,并且您需要一个只消耗CPU的回退,并且希望不做其他事情。例如,参见以下案例:http://shipilev.net/blog/2014/nanotrusting-nanotime/
最初问题中的基准测试不是这样的情况,因此我推测
Blackhole.consumeCPU
在那里使用没有很好的理由,或者至少在评论中没有特别指出这个理由。别这样