JMH - зачем мне Blackhole.consumeCPU()
Я пытаюсь понять, почему это разумно использовать Blackhole.consumeCPU()
?
Что-то, что я нашел о Blackhole.consumeCPU()
в Google ->
Иногда, когда мы запускаем тест производительности в нескольких потоках, мы также хотим записать несколько циклов ЦП, чтобы имитировать нагрузку на процессор при выполнении нашего кода. Это не может быть Thread.sleep, поскольку мы действительно хотим записать процессор. 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)
Я думаю, что теперь я знаю почему, потому что Тесты слишком быстрые, без каких-либо задержек.
С blackhole.consumeCPU(100)
Вы можете измерить каждый тест лучше и получить более значимые результаты.
Это правильно?
1 ответ
Добавление искусственной задержки обычно не улучшит эталонный тест.
Но в некоторых случаях измеряемая операция конкурирует с некоторыми ресурсами, и вам требуется откат, который потребляет только процессор, и, надеюсь, больше ничего не делает. Смотрите, например, дело в: http://shipilev.net/blog/2014/nanotrusting-nanotime/
Эталон в оригинальном вопросе не такой случай, поэтому я бы предположил Blackhole.consumeCPU
используется там без уважительной причины, или, по крайней мере, эта причина специально не упоминается в комментариях. Не делай этого.