0Pricing
Java Academy · Урок

Предотвращение удаления неиспользуемого кода

Blackhole и состояние

«Предотвращение удаления неиспользуемого кода» — бесплатный урок Java Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Как избежать устранения мёртвого кода

Главная ловушка микротестирования — устранение мёртвого кода (DCE): если JIT доказывает, что результат нигде не используется, он удаляет вычисление. Тогда Ваш микротест не измеряет ничего. JMH предоставляет два инструмента для предотвращения этого: возврат значений и Blackhole.

Ловушка устранения мёртвого кода

Этот цикл миллион раз вычисляет квадратный корень, но никогда не использует результат. Умный компилятор может удалить весь цикл.

public class Main {
    public static void main(String[] args) {
        long t = System.nanoTime();
        for (int i = 0; i < 1_000_000; i++) {
            double ignored = Math.sqrt(i); // result thrown away
        }
        System.out.println("ns: " + (System.nanoTime() - t));
    }
}

Исправление 1: возврат результата

Самая простая защита — вернуть вычисленное значение из метода @Benchmark. JMH использует каждое возвращённое значение, поэтому JVM не может доказать, что оно не нужно.

import org.openjdk.jmh.annotations.Benchmark;

public class Bench {
    @Benchmark
    public double sqrt() {
        return Math.sqrt(42.0); // returned -> consumed by JMH
    }
}

Исправление 2: поглотитель

Если микротест создаёт несколько значений или вернуть одно из них невозможно, передайте ему Blackhole и вызовите consume(...). Поглотитель убеждает JIT, что каждое значение используется, не требуя затрат на настоящую работу.

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;

public class Bench {
    @Benchmark
    public void manyValues(Blackhole bh) {
        for (int i = 0; i < 100; i++) {
            bh.consume(Math.sqrt(i));
        }
    }
}

Объекты @State

Класс @State хранит входные данные, непрозрачные для оптимизатора, и тем самым предотвращает свёртку констант. JMH создаёт его экземпляр и передаёт его Вашему микротесту. Область действия может быть Thread, Benchmark (общая) или Group.

import org.openjdk.jmh.annotations.*;

public class Bench {
    @State(Scope.Thread)
    public static class Data {
        public int x = 21;
    }

    @Benchmark
    public int multiply(Data d) {
        return d.x * 2; // d.x is not a compile-time constant
    }
}

@Setup и @TearDown

Внутри класса @State методы с аннотациями @Setup выполняются перед измерением, а методы с аннотацией @TearDown — после него. Level управляет частотой: Trial (один раз), Iteration или Invocation.

import org.openjdk.jmh.annotations.*;

@State(Scope.Thread)
public class Data {
    int[] arr;
    @Setup(Level.Trial)
    public void init() {
        arr = new int[1000];
        for (int i = 0; i < arr.length; i++) arr[i] = i;
    }
}

Избегайте константных входных данных

Никогда не передавайте литеральные константы тестируемому методу. Math.sqrt(2.0) может быть свёрнуто в константу, а Math.sqrt(state.value) — нет. Всегда получайте входные данные из поля @State.

Возврат нескольких значений

Если тест производительности естественным образом выдаёт два результата, можно вернуть один, а другой передать через Blackhole либо объединить их. Если оставить любой из них непотреблённым, снова появляется лазейка для DCE.

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.infra.Blackhole;

public class Bench {
    @Benchmark
    public int twoResults(Blackhole bh) {
        int a = 3 * 7;
        int b = 5 + 9;
        bh.consume(b);
        return a;
    }
}

Накладные расходы Blackhole

Blackhole разработан так, чтобы быть чрезвычайно дешёвым и устойчивым к оптимизации, но он не является полностью бесплатным. Для сверхбыстрых операций лучше возвращать одно значение, а Blackhole использовать в циклах и случаях с несколькими значениями.

Проверяйте правдоподобие чисел

Если тест производительности показывает неправдоподобно малое время (менее наносекунды для настоящей работы), подозревайте DCE или свёртку констант. Проверьте ещё раз, что результаты возвращаются или потребляются, а входные данные поступают из @State.

Собираем всё вместе

Надёжный тест производительности должен читать входные данные из объекта @State, выполнять работу, а затем либо return результат, либо передавать его в Blackhole.consume. Благодаря этим трём привычкам DCE и свёртка констант не смогут исказить результаты измерений.

Быстрая проверка

Проверьте, насколько хорошо Вы умеете предотвращать устранение мёртвого кода.

Итоги

Вы научились делать результаты тестов производительности достоверными:

  • Устранение мёртвого кода удаляет неиспользуемые вычисления.
  • Возвращайте один результат, чтобы JMH его потреблял.
  • Используйте Blackhole.consume для циклов и нескольких значений.
  • Объекты @State скрывают входные данные, предотвращая свёртку констант.
  • @Setup / @TearDown подготавливают и очищают состояние на выбранном уровне.

Часто задаваемые вопросы

Урок «Предотвращение удаления неиспользуемого кода» бесплатный?

Да — полный текст урока «Предотвращение удаления неиспользуемого кода» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Предотвращение удаления неиспользуемого кода»?

Blackhole и состояние Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Предотвращение удаления неиспользуемого кода»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Зачем нужен JMH
  2. Написание измерения производительности
  3. Прогрев и итерации
  4. Предотвращение удаления неиспользуемого кода
← Назад к Java Academy