Ускорение Maven сборки
Узнайте, как сделать сборки Maven более быстрыми и эффективными
Для сборки требуется несколько свойств, главное из которых - воспроизводимость.
Я считаю, что скорость должна быть ниже в порядке приоритета. Тем не менее, это также один из самых лимитирующих факторов на ваш цикл выпуска: если ваша сборка занимает T, вы не можете отпустить быстрее, чем каждый из T.
Следовательно, вы, вероятно, захотите ускорить свои сборки после достижения определенного уровня зрелости, чтобы выпускать более частые релизы.
В этом посте я хочу подробно рассказать о некоторых методах, которые вы можете использовать для ускорения Maven сборки. Следующий пост будет посвящен тому, как сделать то же самое внутри Docker.
Исходный уровень
Поскольку я хочу предложить методы и оценить их влияние, нам нужен образец репозитория.
Я выбрал примеры кода Hazelcast, потому что он обеспечивает достаточно большую многомодульную кодовую базу с множеством подмодулей; точный коммит - 448febd.
Порядок действий следующий:
Я запускаю команду пять раз, чтобы избежать временных проблем
Я выполняю
mvn clean
перед каждым запуском, чтобы начать с пустогоtarget
репозиторияВсе зависимости и плагины уже загружены
Я сообщаю время, которое Maven отображает в журнале консоли:
[INFO] ------------------------------------------------------- [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------- [INFO] Total time: 22.456 s (Wall Clock) [INFO] Finished at: 2021-09-24T23:20:41+02:00 [INFO] -------------------------------------------------------
Начнем с нашего исходного уровня, выполнив команду: mvn test
. Результат:
02:00 мин.
01:57 мин.
01:58 мин.
01:56 мин.
01:58 мин.
Использование всех процессоров
По умолчанию Maven использует один поток. В век многоядерных процессоров это пустая трата времени. Можно запускать параллельные сборки с использованием нескольких потоков, задав абсолютное число или число относительно количества доступных ядер. Для получения дополнительной информации, пожалуйста, посмотрите соответствующую документацию.
Чем больше у вас подмодулей, которые не зависят друг от друга, т.е. Maven может выполнять их сборку параллельно, тем лучше результат вы получите с помощью этой техники.
Это очень хорошо работает на нашей кодовой базе.
Мы собираемся использовать столько потоков, сколько доступно ядер. Соответствующая команда Maven: mvn test -T 1C
.
При запуске этой команды вы должны увидеть в консоли следующее сообщение:
Using the MultiThreadedBuilder implementation with a thread count of X
51.487 s (Wall Clock)
40.322 s (Wall Clock)
52.468 s (Wall Clock)
41.862 s (Wall Clock)
41.699 s (Wall Clock)
Цифры намного лучше, но с большей дисперсией.
Параллельное выполнение теста
Распараллеливание - отличный метод. Мы можем сделать то же самое в отношении выполнения тестов. По умолчанию плагин Maven Surefire запускает тесты последовательно, но можно настроить его для параллельного запуска тестов. Пожалуйста, обратитесь к документации за полным набором опций.
Этот подход отлично подходит, если у вас есть большое количество юнитов в каждом модуле. Обратите внимание, что ваши тесты должны быть независимыми друг от друга.
Мы вручную зададим количество потоков:
mvn test -Dparallel=all -DperCoreThreadCount=false -DthreadCount=16 #1 #2
Настройте Surefire для параллельного запуска классов и методов
Ручное изменение количества потоков до 16
Запустим и получим:
02:04 мин.
02:03 мин.
01:46 мин
01:52 мин
01:53 мин.
Похоже, что стоимость синхронизации потоков компенсирует потенциальную выгоду от выполнения параллельных тестов.
Офлайн
Maven будет проверять, есть ли у SNAPSHOT
зависимости новая «версия» при каждом запуске. Это означает дополнительные сетевые запросы. Мы можем предотвратить эту проверку с помощью опции --offline
.
Хотя вам следует избегать SNAPSHOT
зависимостей, иногда это неизбежно, особенно во время разработки.
Команда mvn test -o
, -o
является сокращением для --offline
.
01:46 мин
01:46 мин
01:47 мин
01:55 мин.
01:44 мин
Кодовая база имеет большое количество SNAPSHOT
зависимостей; следовательно, автономный режим значительно ускоряет сборку.
Параметры JVM
Сам Maven - это приложение на основе Java. Это означает, что каждый запуск запускает новую JVM. JVM сначала интерпретирует байт-код, а затем анализирует рабочий код и соответственно компилирует байт-код в нативный код: это обеспечивает максимальную производительность, но только после длительного времени сборки. Это отлично подходит для длительно выполняющихся процессов, а не для приложений командной строки.
Скорее всего, мы не достигнем максимальной производительности в контексте сборок, поскольку они относительно недолговечны, но мы по-прежнему расплачиваемся за время анализа.
Мы можем настроить Maven, чтобы отказаться от него, настроив соответствующие параметры JVM. Доступно несколько способов настройки JVM. Самый простой способ - создать специальный jvm.config
файл конфигурации во вложенной папке .mvn
проекта.
-XX: -TieredCompilation -XX: TieredStopAtLevel = 1
Давайте теперь просто запустим mvn test
:
01:44 мин
01:44 мин
01:53 мин.
01:53 мин.
01:55 мин.
Maven Daemon
Демон Maven является недавним дополнением к экосистеме Maven. Он инспирирован демоном Gradle :
Gradle работает на виртуальной машине Java (JVM) и использует несколько вспомогательных библиотек, для которых требуется значительное время инициализации.
В результате это иногда запуск может оказаться немного замедленным. Решением этой проблемы является Gradle Daemon: долгосрочный фоновый процесс, который выполняет ваши сборки намного быстрее, чем без него. Это достигается исключая дорогостоящий процесс начальной загрузки и используя кеширование для сохранения данных о вашем проекте в памяти.
Команда Gradle осознала, что инструмент командной строки - не лучшее применение JVM. Чтобы решить эту проблему, нужно постоянно поддерживать фоновый процесс JVM, используя демон. Он работает как сервер, а интерфейс командной строки играет роль клиента.
В качестве дополнительного преимущества этот длительный процесс загружает классы только один раз (если они не менялись между запусками).
После установки программного обеспечения вы можете запустить демон с помощью mvnd
команды вместо стандартной mvn
. Вот результаты выполнения команды: mvnd test
:
33.124 s (Wall Clock)
33.114 s (Wall Clock)
34.440 s (Wall Clock)
32.025 s (Wall Clock)
29.364 s (Wall Clock)
Обратите внимание, что по умолчанию демон использует несколько потоков с расширением number of cores - 1
.
Объединение методов
Мы видели несколько способов ускорить сборку. Что, если бы мы использовали их вместе?
Давайте сначала попробуем использовать все методы, которые мы видели до сих пор, в одном и том же прогоне:
mvnd test -Dparallel=all -DperCoreThreadCount=false -DthreadCount=16 -o #1 #2 #3 #4
Используем демон Maven
Запускаем тесты параллельно
Не обновляем
SNAPSHOT
зависимостиНастраиваем параметры JVM, как указано выше, через
jvm.config
файл - нет необходимости устанавливать какие-либо параметры
Команда возвращает следующие результаты:
27.061 s (Wall Clock)
24.457 s (Wall Clock)
24.853 s (Wall Clock)
25.772 s (Wall Clock)
Давайте вспомним, что демон Maven - это длительный процесс. По этой причине разумно позволить JVM проанализировать и скомпилировать байт-код в нативный код. Таким образом, мы можем удалить jvm.config
файл и повторно запустить указанную выше команду. Результаты:
23.840 s (Wall Clock)
26.589 s (Wall Clock)
22.283 s (Wall Clock)
23.788 s (Wall Clock)
22.456 s (Wall Clock)
Теперь мы можем отобразить сводные результаты:
Baseline | Parallel Build | Parallel Tests | Offline | Jvm Params | Daemon | Daemon + Offline + Parallel Tests + Parameters | Daemon + Offline + Parallel Tests | |
#1 (s) | 120.00 | 51.00 | 128.00 | 106.00 | 104.00 | 33.12 | 27.06 | 23.84 |
#2 (s) | 117.00 | 40.00 | 123.00 | 106.00 | 104.00 | 33.11 | 24.46 | 26.59 |
#3 (s) | 118.00 | 52.00 | 106.00 | 107.00 | 113.00 | 34.44 | 24.85 | 22.28 |
#4 (s) | 116.00 | 42.00 | 112.00 | 115.00 | 113.00 | 32.03 | 25.77 | 23.79 |
#5 (s) | 118.00 | 42.00 | 113.00 | 104.00 | 115.00 | 29.36 | 22.46 | |
Average (s) | *117.80* | 45.40 | 116.40 | 107.60 | 109.80 | 32.41 | 25.54 | 23.79 |
Deviation | 1.76 | 25.44 | 63.44 | 14.64 | 22.96 | 2.91 | 1.00 | 2.38 |
Gain from baseline (s) | - | 72.40 | 1.40 | 10.20 | 8.00 | 85.39 | 92.26 | 94.01 |
% gain | - | 61.46% | 1.19% | 8.66% | 6.79% | 72.48% | 78.32% | 79.80% |
Заключение
В этом посте мы увидели несколько способов ускорить сборку Maven. Вот их краткое изложение:
Демон Maven: надежная и безопасная отправная точка
Распараллеливание сборки: когда сборка содержит несколько модулей, независимых друг от друга.
Распараллеливание тестов: когда проект содержит несколько тестов.
Автономность: когда проект содержит
SNAPSHOT
зависимости и вам не нужно их обновлять.Параметры JVM: когда вы хотите сделать все возможное
Я бы посоветовал каждому пользователю начать использовать демон Maven и продолжить оптимизацию при необходимости и в зависимости от вашего проекта.
В следующем посте мы сосредоточимся на ускорении ваших сборок Maven в контейнере.
Чтобы продвинуться дальше, прочтите: