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 используется там без уважительной причины, или, по крайней мере, эта причина специально не упоминается в комментариях. Не делай этого.

Другие вопросы по тегам