Опровергнуть «Ошибку в «Руководстве по разработке Java» Али?

Java оптимизация производительности
Опровергнуть «Ошибку в «Руководстве по разработке Java» Али?

Два дня назад я написал статью о«Ошибка в руководстве по разработке Java для Ali»статьи, область комментариев немного жарит, в основном разделена на две фракции: те, кто поддерживает Лао Вана, и те, кто сомневается в Лао Ване.

Прежде всего, независимо от того, с какой стороны, я искренне благодарю вас. В частности, «Второй старший брат» изначально планировал хорошенько отдохнуть в пятницу вечером (статья, опубликованная в пятницу вечером), но поскольку он обсуждал со мной этот вопрос, то получил его примерно до 12 часов вечера. видно, что он хорошо разбирается в технике. Мы такие же в этом вопросе, и, как и вы, читающие эту статью,Мы относимся к категории людей, бесконечно одержимых технологиями.

img

Значение правильного и неправильного

На самом деле, когда я готовился опубликовать эту статью, я уже предвидел эту ситуацию.Когда вы вопрошаете, не важно, правильно это или неправильно, должны быть противоположные голоса, потому что у других тоже есть право на вопрос, и что вы должны делать в этот момент стоит постараться сохранять Спокойствие, понять и проверить эти вопросы с объективным отношением. И эти вопросы (разные голоса) со временем станут ценным активом, потому что вы обязательно что-то приобретете в этом процессе проверки.

В то же время, я также надеюсь, что мое понимание ошибочно, потому что, как и все, я также являюсь верным "верующим" "Руководству по разработке Java" Али, но я случайно мельком увидел "разницу", а затем поделился своими идеями и результат у всех..

Но я также считаю, что любой "авторитет" имеет возможность ошибаться. Наши предки когда-то говорили нам, что "люди, которые не являются святыми и мудрецами, могут ошибаться". Мне не нужно беспокоиться о том, кто прав, а кто виноват, наоборотЯ считаю, что слепо гоняться за тем, кто прав, а кто виноват, очень наивно., только дети делают это, что мы должны сделать, этоОбсуждая «правильно и неправильно» в этом вопросе, мы можем получить больше знаний и помочь нам стать лучше., в этом смысл моей сегодняшней статьи.

Джобс однажды сказал:Мне больше всего нравится работать с умными людьми, потому что им не нужно беспокоиться о своем достоинстве. Я не умный человек, но я знаю, что за любой «неправильной вещью» должна стоять какая-то ценность. Поэтому я не боюсь получить "пощечину".Если вы хотите быстро расти, советую вам сделать то же самое.

Ладно, на этом пока все, давайте перейдем к сегодняшней теме.

оппозиция

Основные пункты друзей, придерживающихся разных взглядов, следующие:

img

img

Я разобрал эти комментарии.На самом деле я сказал одно.Давайте сначала посмотрим на содержание исходного текста.

В «Руководстве по разработке Java» Taishan Edition (последнее издание) это указано в четвертой спецификации третьего подраздела второй главы:

[Обязательно] Используйте заполнители для объединения строковых переменных во время вывода журнала.

Примечание. Поскольку при сращивании строк String будет использоваться метод append() StringBuilder, производительность несколько снижается. Использование заполнителей — это только заменяющее действие, которое может эффективно улучшитьпредставление.

Положительный пример: logger.debug("Обработка сделки с id: {} и symbol: {}", id, symbol);

Оппонент (обратите внимание, что это «оппозиция» — не уничижительный термин, а для лучшего разграничения ролей) означает это:

Использование заполнителя сначала оценивает выходной уровень журнала, а затем решает, следует ли объединять выходные данные, в то время как прямое использование StringBuilder сначала выполняет объединение, а затем оценку, в этом случае, когда уровень журнала установлен относительно высокий, потому что StringBuilder объединяется. Сначала он оценивается снова, что приводит к пустой трате системных ресурсов, поэтому способ использования заполнителей имеет более высокую производительность, чем способ StringBuilder.

Отложим в сторону, отражен ли упомянутый оппонентами смысл в "Руководстве по разработке Java" Али, потому что я действительно его не видел, проследим за этим ходом размышлений, чтобы подтвердить правильность вывода.

Оценка эффективности

Это все еще старое правило, давайте поговорим с данными и кодом, Чтобы проверить, что JMH (инструмент тестирования) может хорошо сочетаться с Spring Boot, первое, что нам нужно сделать, это проверить, может ли настройка уровня вывода журнала вступить в силу. в тестовом коде JMH.

Тогда давайте сначала напишем тестовый код для «установки уровня журнала»:

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.annotations.*;
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;
import org.springframework.boot.SpringApplication;

import java.util.concurrent.TimeUnit;


@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 2s
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
@Slf4j
public class LogPrintAmend {
    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(LogPrintAmend.class.getName() + ".*") // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        // 启动 spring boot
        SpringApplication.run(DemoApplication.class);
    }

    @Benchmark
    public void logPrint() {
        log.debug("show debug");
        log.info("show info");
        log.error("show error");
    }
}

В тестовом коде мы используем 3 уровня директив логирования вывода:debugуровень,infoуровень иerrorуровень.

Затем задаем уровень вывода лога в конфигурационном файле (application.properties), конфигурация следующая:

logging.level.root=info

Видно, что мы установили все уровни вывода лога наinfoуровне, а затем выполняем указанную выше программу, результат выполнения следующий:

Как видно из рисунка выше, журнал выводит толькоinfoиerrorlevel, то есть установленный нами уровень вывода лога вступает в силу.Для обеспечения защиты от дурака мы снизим уровень вывода лога доdebugуровень, результаты теста показаны на следующем рисунке:

Как видно из приведенных выше результатов, в установленном нами уровне логирования нет ничего плохого, т.е.Фреймворк JMH можно хорошо использовать с SpringBoot..

Советы, вес уровня журнала: TRACE

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

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.annotations.*;
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;
import org.springframework.boot.SpringApplication;

import java.util.concurrent.TimeUnit;


@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 2s
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
@Slf4j
public class LogPrintAmend {

    private final static int MAX_FOR_COUNT = 100; // for 循环次数

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(LogPrintAmend.class.getName() + ".*") // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        SpringApplication.run(DemoApplication.class);
    }

    @Benchmark
    public void appendLogPrint() {
        for (int i = 0; i < MAX_FOR_COUNT; i++) { // 循环的意图是为了放大性能测试效果
            // 先拼接
            StringBuilder sb = new StringBuilder();
            sb.append("Hello, ");
            sb.append("Java");
            sb.append(".");
            sb.append("Hello, ");
            sb.append("Redis");
            sb.append(".");
            sb.append("Hello, ");
            sb.append("MySQL");
            sb.append(".");
            // 再判断
            if (log.isInfoEnabled()) {
                log.info(sb.toString());
            }
        }
    }

    @Benchmark
    public void logPrint() {
        for (int i = 0; i < MAX_FOR_COUNT; i++) { // 循环的意图是为了放大性能测试效果
            log.info("Hello, {}.Hello, {}.Hello, {}.", "Java", "Redis", "MySQL");
        }
    }
}

Видно, что используется кодinfoуровень данных журнала, то мы устанавливаем уровень журнала в файле конфигурации больше, чемinfoизerrorуровне, а затем выполните приведенный выше код, результаты теста будут следующими:

img

Вау, какой удовлетворительный результат теста. Из приведенных выше результатов мы видим, что производительность использования заполнителей действительно лучше, чемStringBuilderПуть гораздо выше, а значит, "Руководство по разработке Java" Али не проблема.

обеспечить регресс

Но все не так просто, например, когда вы идете по дороге, к вам приближается велосипед и он вот-вот столкнется с вами, что вы будете делать в это время? Без сомнения, вы будете подсознательно избегать этого.

Так что то же самое верно и для приведенной выше оценки, зачем судить после сращивания строк?

Если программирование уже является вашим официальным занятием, то оценка, а затем сращивание строк является самым основным требованием к профессиональным навыкам. Это та же причина, по которой вы будете подсознательно избегать велосипеда, который столкнулся лоб в лоб. Если вы полностью способны, Избегая проблем , вы должны сначала избежать проблем, а потом выполнять другие операции, иначе, когда команда проверит код или уволит сотрудников в конце месяца, вы должны быть первым «пострадавшим» объектом. Потому что такие простые (неправильные) вопросы возможны только для новичков, которые только начинают.

Тогда в соответствии с самыми основными требованиями программы мы должны написать такой код:

import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.annotations.*;
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;
import org.springframework.boot.SpringApplication;

import java.util.concurrent.TimeUnit;


@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 2s
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
@Slf4j
public class LogPrintAmend {

    private final static int MAX_FOR_COUNT = 100; // for 循环次数

    public static void main(String[] args) throws RunnerException {
        // 启动基准测试
        Options opt = new OptionsBuilder()
                .include(LogPrintAmend.class.getName() + ".*") // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Setup
    public void init() {
        SpringApplication.run(DemoApplication.class);
    }

    @Benchmark
    public void appendLogPrint() {
        for (int i = 0; i < MAX_FOR_COUNT; i++) { // 循环的意图是为了放大性能测试效果
            // 再判断
            if (log.isInfoEnabled()) {
                StringBuilder sb = new StringBuilder();
                sb.append("Hello, ");
                sb.append("Java");
                sb.append(".");
                sb.append("Hello, ");
                sb.append("Redis");
                sb.append(".");
                sb.append("Hello, ");
                sb.append("MySQL");
                sb.append(".");
                log.info(sb.toString());
            }
        }
    }

    @Benchmark
    public void logPrint() {
        for (int i = 0; i < MAX_FOR_COUNT; i++) { // 循环的意图是为了放大性能测试效果
            log.info("Hello, {}.Hello, {}.Hello, {}.", "Java", "Redis", "MySQL");
        }
    }
}

даже поставитьifУпомянутое решениеforвне цикла, ноforОн представляет собой не конкретный бизнес, а код, написанный для лучшего усиления тестового эффекта, поэтому мы напишем суждение вforвнутри петли.

Затем давайте снова выполним тестовый код в это время, и результат выполнения будет таким, как показано на следующем рисунке:

img

Из приведенных выше результатов видно, что метод использования первой оценки, а затем объединения строк все еще более эффективен, чем использование заполнителей.

Так,У нас до сих пор нет возможности доказать высокую эффективность плейсхолдеров в «Руководстве по разработке Java» Али..

Так что я все еще придерживаюсь своего мнения,Использование заполнителей вместо сращивания строк может в основном обеспечить элегантность кода, и вы можете делать менее логические суждения в коде, но это не имеет ничего общего с производительностью..

Расширенные знания: форматирование журналов

В приведенном выше процессе оценки мы обнаружили, что выходной формат журнала очень "хаотичен". Есть ли способ отформатировать журнал?

Ответ: да, вывод журнала по умолчанию выглядит следующим образом:

img

Можно настроить для форматирования журнала весеннего загрузкиlogging.pattern.consoleварианты, пример конфигурации выглядит следующим образом:

logging.pattern.console=%d | %msg %n

Вывод журнала выглядит следующим образом:

img

Видно, что после форматирования журнала содержимое простое, но нельзя отказываться от ключевой отладочной информации из-за простоты и пропускать вывод.

Суммировать

В этой статье мы протестировали оценку производительности сплайсинга строк и заполнителей, заданную читателями, и обнаружили, что точка зрения о том, что метод заполнителя имеет высокую производительность, до сих пор не может быть проверена, поэтому наша основная точка зрения по-прежнему такова.Способ использования плейсхолдеров более элегантен, и можно добиться большего количества функций с меньшим количеством кода; что касается производительности, то можно сказать только, что она неплохая, но и не отличная.. В конце статьи мы говорили о знании форматирования журнала Spring Boot. Я надеюсь, что эта статья может помочь вам практически. Вы также можете оставить сообщение в области комментариев, чтобы пообщаться со мной.

последние слова

Оригинальность непростая, вы видели это, нажмите "отличный«Пойдем еще раз, это самая большая поддержка и ободрение для меня, спасибо!

Подпишитесь на официальный аккаунт «Java Chinese Community» и ответьте на «Галантные товары», чтобы получить 50 оригинальных галантерейных товаров.Топ-лист.