Компиляция и кэши в плагине Kotlin Gradle
На этой странице вы можете узнать о следующих темах:
Инкрементальная компиляция
Плагин Kotlin Gradle поддерживает инкрементальную компиляцию. Инкрементальная компиляция отслеживает изменения исходных файлов между сборками, так что компилируются только файлы, затронутые этими изменениями.
Инкрементальная компиляция поддерживается для проектов Kotlin/JVM и Kotlin/JS и включена по умолчанию.
Существует несколько способов отключить инкрементальную компиляцию:
Установите
kotlin.incremental=falseдля Kotlin/JVM.Установите
kotlin.incremental.js=falseдля проектов Kotlin/JS.-
Используйте
-Pkotlin.incremental=falseили-Pkotlin.incremental.js=falseв качестве параметра командной строки.Параметр должен быть добавлен в каждую последующую сборку.
Примечание: любая сборка с отключенной инкрементальной компиляцией делает кэш инкрементальных данных недействительным. Первая сборка никогда не является инкрементальной.
Новый подход к инкрементальной компиляции
Новый подход к инкрементальной компиляции доступен с Kotlin 1.7.0 для JVM-бэкэнда в системе сборки Gradle только. Начиная с Kotlin 1.8.20, он включен по умолчанию. Этот подход поддерживает изменения, внесенные внутри зависимых модулей, не написанных на Kotlin, включает улучшенное избегание компиляции и совместим с кэшем сборки Gradle.
Все эти улучшения уменьшают количество неинкрементальных сборок, что делает общую скорость компиляции быстрее. Вы получите наибольшую выгоду, если будете использовать кэш сборки или часто вносить изменения в модули Gradle, не написанные на Kotlin.
Чтобы отказаться от этого нового подхода, установите следующий параметр в своем gradle.properties:
kotlin.incremental.useClasspathSnapshot=false
Мы будем признательны за ваши отзывы об этой функции в YouTrack.
Узнайте, как реализован новый подход к инкрементальной компиляции, в этой статье блога.
Точное резервное копирование выходных данных задач компиляции
Начиная с Kotlin 1.8.20, вы можете включить точное резервное копирование, при котором резервную копию создают только те классы, которые Kotlin перекомпилирует при инкрементальной компиляции. Как полные, так и точные резервные копии помогают запустить сборки инкрементально снова после ошибок компиляции. Точное резервное копирование занимает меньше времени сборки по сравнению с полным резервным копированием. Полное резервное копирование может потребовать значительно больше времени сборки в больших проектах или если много задач создают резервные копии, особенно если проект находится на медленном жёстком диске.
Включите эту оптимизацию, добавив свойство Gradle kotlin.compiler.preciseCompilationResultsBackup в файл gradle.properties:
kotlin.compiler.preciseCompilationResultsBackup=true
Пример использования точного резервного копирования в JetBrains
В следующих диаграммах вы можете увидеть примеры использования точного резервного копирования по сравнению с полным резервным копированием:

Первая и вторая диаграммы показывают, как использование точного резервного копирования в проекте Kotlin влияет на сборку плагина Kotlin Gradle:
После внесения небольшого изменения ABI: добавление нового публичного метода в модуль, от которого зависят многие модули.
После внесения небольшого изменения, не связанного с ABI: добавление приватной функции в модуль, от которого не зависят другие модули.
Третья диаграмма показывает, как точное резервное копирование в проекте Space влияет на сборку веб-фронта после небольшого изменения, не связанного с ABI: добавление приватной функции в модуль Kotlin/JS, от которого зависят многие модули.
Эти измерения были проведены на компьютере с процессором Apple M1 Max; на разных компьютерах результаты будут немного отличаться. Факторы, влияющие на производительность, включают, но не ограничиваются:
Насколько разогрет демон Kotlin и демон Gradle.
Насколько быстрым или медленным является диск.
Модель процессора и насколько он загружен.
Какие модули затронуты изменениями и насколько они велики.
Являются ли изменения ABI или нет.
Оценка оптимизаций с помощью отчетов о сборке
Чтобы оценить влияние оптимизации на вашем компьютере для вашего проекта и ваших сценариев, вы можете использовать отчеты о сборке Kotlin. Включите отчеты в формате текстового файла, добавив следующее свойство в ваш файл gradle.properties:
kotlin.build.report.output=file
Вот пример релевантной части отчета до включения точного резервного копирования:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.59 s <...> Time metrics: Total Gradle task time: 0.59 s Task action before worker execution: 0.24 s Backup output: 0.22 s // Pay attention to this number <...>
И вот пример релевантной части отчета после включения точного резервного копирования:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.46 s <...> Time metrics: Total Gradle task time: 0.46 s Task action before worker execution: 0.07 s Backup output: 0.05 s // The time has reduced Run compilation in Gradle worker: 0.32 s Clear jar cache: 0.00 s Precise backup output: 0.00 s // Related to precise backup Cleaning up the backup stash: 0.00 s // Related to precise backup <...>
Поддержка кэша сборки Gradle
Плагин Kotlin использует кэш сборки Gradle, который хранит выходные данные сборки для повторного использования в будущих сборках.
Чтобы отключить кэширование для всех задач Kotlin, установите системную переменную kotlin.caching.enabled в значение false (запустите сборку с аргументом -Dkotlin.caching.enabled=false).
Поддержка кэша конфигурации Gradle
Плагин Kotlin использует кэш конфигурации Gradle, который ускоряет процесс сборки, повторно используя результаты фазы конфигурации.
См. документацию Gradle, чтобы узнать, как включить кэш конфигурации. После включения этой функции плагин Kotlin Gradle автоматически начинает его использовать.
Kotlin-демон и как использовать его с Gradle
Kotlin-демон:
Запускается вместе с Gradle-демоном для компиляции проекта.
Запускается отдельно от Gradle-демона, когда вы компилируете проект с интегрированной системой сборки IntelliJ IDEA.
Kotlin-демон запускается на этапе выполнения Gradle Gradle execution stage, когда одна из задач компиляции Kotlin начинает компилировать исходные файлы. Kotlin-демон останавливается либо вместе с Gradle-демоном, либо через два часа бездействия без компиляции Kotlin.
Kotlin-демон использует ту же JDK, что и Gradle-демон.
Настройка JVM-аргументов Kotlin-демона
Каждый из следующих способов настройки аргументов переопределяет предыдущие:
Наследование аргументов Gradle-демона
Если ничего не указано, Kotlin-демон наследует аргументы от Gradle-демона. Например, в файле gradle.properties:
org.gradle.jvmargs=-Xmx1500m -Xms=500m
Системная переменная kotlin.daemon.jvm.options
Если у Gradle-демона есть JVM-аргументы с kotlin.daemon.jvm.options системной переменной, используйте её в файле gradle.properties:
org.gradle.jvmargs=-Dkotlin.daemon.jvm.options=-Xmx1500m,Xms=500m
При передаче аргументов следуйте этим правилам:
Используйте знак минус
-только перед аргументамиXmx,XX:MaxMetaspaceSizeиXX:ReservedCodeCacheSize.Разделяйте аргументы запятыми (
,) без пробелов. Аргументы, следующие за пробелом, будут использоваться для Gradle-демона, а не для Kotlin-демона.
Свойство kotlin.daemon.jvmargs
Вы можете добавить kotlin.daemon.jvmargs свойство в файле gradle.properties:
kotlin.daemon.jvmargs=-Xmx1500m -Xms=500m
Расширение kotlin
Вы можете указать аргументы в расширении kotlin:
kotlin {
kotlinDaemonJvmArgs = listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC")
}
kotlin {
kotlinDaemonJvmArgs = ["-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"]
}
Конкретное определение задачи
Вы можете указать аргументы для конкретной задачи:
tasks.withType<CompileUsingKotlinDaemon>().configureEach {
kotlinDaemonJvmArguments.set(listOf("-Xmx486m", "-Xms256m", "-XX:+UseParallelGC"))
}
tasks.withType(CompileUsingKotlinDaemon::class).configureEach { task ->
task.kotlinDaemonJvmArguments.set(["-Xmx1g", "-Xms512m"])
}
Поведение Kotlin-демона с JVM-аргументами
При настройке JVM-аргументов Kotlin-демона обратите внимание на:
Ожидается одновременное выполнение нескольких экземпляров Kotlin-демона, когда разные подпроекты или задачи имеют разные наборы JVM-аргументов.
-
Новый экземпляр Kotlin-демона запускается только при выполнении Gradle-задачи, связанной с компиляцией, и если у существующих Kotlin-демонов нет одинакового набора JVM-аргументов. Представьте, что у вашего проекта много подпроектов. Большинство из них требуют определённого объёма памяти для Kotlin-демона, но один модуль требует много (хотя он редко компилируется). В таком случае вы должны указать разный набор JVM-аргументов для такого модуля, чтобы Kotlin-демон с большим объёмом памяти запускался только для разработчиков, работающих с этим конкретным модулем.
Если аргумент
Xmxне указан, Kotlin-демон унаследует его от Gradle-демона.
Новый компилятор Kotlin
Новый компилятор Kotlin K2 находится в стадии Альфа. Он имеет базовую поддержку проектов Kotlin JVM, JS и Native.
Новый компилятор призван ускорить разработку новых языковых функций, унифицировать все поддерживаемые Kotlin платформы, улучшить производительность и предоставить API для расширений компилятора.
Компилятор K2 станет стандартным по умолчанию начиная с Kotlin 2.0. Чтобы попробовать его в своих проектах сейчас и проверить производительность, используйте свойство Gradle kotlin.experimental.tryK2=true или выполните следующую команду:
./gradlew assemble -Pkotlin.experimental.tryK2=true
Это свойство Gradle автоматически устанавливает версию языка по умолчанию в 2.0 и обновляет отчёт о сборке количеством задач Kotlin, скомпилированных с помощью компилятора K2 по сравнению с текущим компилятором.
Узнайте больше о стабилизации компилятора K2 в нашем блоге Kotlin
Определение стратегии выполнения компилятора Kotlin
Стратегия выполнения компилятора Kotlin определяет, где выполняется компилятор Kotlin, и поддерживается ли инкрементная компиляция в каждом случае.
Существует три стратегии выполнения компилятора:
Стратегия |
Место выполнения компилятора Kotlin |
Инкрементная компиляция |
Другие характеристики и заметки |
|---|---|---|---|
Демон |
В собственном процессе демона |
Да |
По умолчанию и самая быстрая стратегия. Может быть использована различными демонами Gradle и для нескольких параллельных компиляций. |
В процессе |
В процессе демона Gradle |
Нет |
Может использовать кучу с демоном Gradle. Стратегия выполнения «В процессе» медленнее, чем стратегия «Демон». Каждый работник создает отдельный класслоадер компилятора Kotlin для каждой компиляции. |
Вне процесса |
В отдельном процессе для каждой компиляции |
Нет |
Самая медленная стратегия выполнения. Аналогична стратегии «В процессе», но дополнительно создает отдельный процесс Java внутри работника Gradle для каждой компиляции. |
Для определения стратегии выполнения компилятора Kotlin можно использовать следующие свойства:
Свойство Gradle
kotlin.compiler.execution.strategy.Свойство задачи
compilerExecutionStrategycompile.
Свойство задачи compilerExecutionStrategy имеет приоритет над свойством Gradle kotlin.compiler.execution.strategy.
Доступные значения для свойства kotlin.compiler.execution.strategy:
daemon(по умолчанию)in-processout-of-process
Используйте свойство Gradle kotlin.compiler.execution.strategy в gradle.properties:
kotlin.compiler.execution.strategy=out-of-process
Доступные значения для свойства задачи compilerExecutionStrategy:
org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.DAEMON(по умолчанию)org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.IN_PROCESSorg.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.OUT_OF_PROCESS
Используйте свойство задачи compilerExecutionStrategy в скриптах сборки:
import org.jetbrains.kotlin.gradle.tasks.CompileUsingKotlinDaemon
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
// ...
tasks.withType<CompileUsingKotlinDaemon>().configureEach {
compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}
import org.jetbrains.kotlin.gradle.tasks.CompileUsingKotlinDaemon
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
// ...
tasks.withType(CompileUsingKotlinDaemon)
.configureEach {
compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}
Стратегия резервного копирования компилятора Kotlin
Стратегия резервного копирования компилятора Kotlin заключается в запуске компиляции вне демона Kotlin, если демон каким-то образом терпит неудачу. Если демон Gradle включен, компилятор использует стратегию «В процессе». Если демон Gradle выключен, компилятор использует стратегию «Вне процесса».
При такой резервной копии в выводе сборки Gradle появятся следующие предупреждающие строки:
Failed to compile with Kotlin daemon: java.lang.RuntimeException: Could not connect to Kotlin compile daemon [exception stacktrace] Using fallback strategy: Compile without Kotlin daemon Try ./gradlew --stop if this issue persists.
Однако, пассивное резервное копирование в другую стратегию может сильно нагрузить систему или привести к непредсказуемым результатам сборки. Подробнее об этом можно прочитать в заявке YouTrack. Чтобы этого избежать, существует свойство Gradle kotlin.daemon.useFallbackStrategy, значение которого по умолчанию равно true. При значении false сборка завершается неудачей при проблемах с запуском или общением демона. Объявите это свойство в gradle.properties:
kotlin.daemon.useFallbackStrategy=false
Также существует свойство useDaemonFallbackStrategy в задачах компиляции Kotlin, которое имеет приоритет над свойством Gradle, если вы используете оба.
tasks {
compileKotlin {
useDaemonFallbackStrategy.set(false)
}
}
tasks.named("compileKotlin").configure {
useDaemonFallbackStrategy = false
}
Если для выполнения компиляции недостаточно памяти, в логах появится сообщение об этом.
Отчёты о сборке
Отчёты о сборке содержат продолжительность различных фаз компиляции и причины, по которым компиляция не была инкрементальной. Используйте отчёты о сборке, чтобы исследовать проблемы производительности, когда время компиляции слишком долго или отличается для одного и того же проекта.
Отчёты о сборке Kotlin помогут вам более эффективно исследовать проблемы с производительностью сборки, чем с помощью Gradle build scans, которые используют отдельную задачу Gradle в качестве единицы измерения.
Есть два распространённых случая, когда анализ отчётов о сборке для длительных компиляций может помочь вам в решении проблем:
Сборка не была инкрементальной. Проанализируйте причины и исправьте лежащие в основе проблемы.
Сборка была инкрементальной, но заняла слишком много времени. Попробуйте переорганизовать исходные файлы — разделить большие файлы, сохранить отдельные классы в разных файлах, переработать большие классы, объявить функции верхнего уровня в разных файлах и так далее.
Отчёты о сборке также показывают версию Kotlin, используемую в проекте. Кроме того, начиная с Kotlin 1.9.0, вы можете увидеть, использовался ли текущий или компилятор K2 для компиляции кода в вашем Gradle build scan.
Узнайте как читать отчёты о сборке и о том, как JetBrains использует отчёты о сборке.
Включение отчётов о сборке
Чтобы включить отчёты о сборке, укажите, где сохранить вывод отчёта о сборке в gradle.properties:
kotlin.build.report.output=file
Доступны следующие значения и их комбинации для вывода:
Опция |
Описание |
|
|---|---|---|
|
Сохраняет отчёты о сборке в удобочитаемом формате в локальный файл. По умолчанию — |
|
|
Сохраняет отчёты о сборке в формате объекта в указанный локальный файл |
|
|
Сохраняет отчёты о сборке в разделе |
|
|
Отправляет отчёты о сборке с помощью HTTP(S). Метод POST отправляет метрики в формате JSON. Текущую версию отправленных данных можно увидеть в репозитории Kotlin. Примеры HTTP-точек входа можно найти в этой статье блога |
Вот список доступных вариантов для kotlin.build.report:
# Required outputs. Any combination is allowed kotlin.build.report.output=file,single_file,http,build_scan # Mandatory if single_file output is used. Where to put reports # Use instead of the deprecated `kotlin.internal.single.build.metrics.file` property kotlin.build.report.single_file=some_filename # Optional. Output directory for file-based reports. Default: build/reports/kotlin-build/ kotlin.build.report.file.output_dir=kotlin-reports # Optional. Label for marking your build report (for example, debug parameters) kotlin.build.report.label=some_label
Опции, применимые только к HTTP:
# Mandatory. Where to post HTTP(S)-based reports kotlin.build.report.http.url=http://127.0.0.1:8080 # Optional. User and password if the HTTP endpoint requires authentication kotlin.build.report.http.user=someUser kotlin.build.report.http.password=somePassword # Optional. Add a Git branch name of a build to a build report kotlin.build.report.http.include_git_branch.name=true|false # Optional. Add compiler arguments to a build report # If a project contains many modules, its compiler arguments in the report can be very heavy and not that helpful kotlin.build.report.include_compiler_arguments=true|false
Предел настраиваемых значений
Для сбора статистики build scans отчёты о сборке Kotlin используют настраиваемые значения Gradle. Как вы, так и различные плагины Gradle могут записывать данные в настраиваемые значения. Количество настраиваемых значений ограничено. См. текущее максимальное количество настраиваемых значений в документации плагина Build scan.
Если у вас большой проект, количество таких настраиваемых значений может быть достаточно большим. Если это число превышает предел, вы можете увидеть следующее сообщение в логах:
Maximum number of custom values (1,000) exceeded
Чтобы уменьшить количество настраиваемых значений, создаваемых плагином Kotlin, вы можете использовать следующее свойство в gradle.properties:
kotlin.build.report.build_scan.custom_values_limit=500
Отключение сбора свойств проекта и системы
Логи статистических отчётов о сборке HTTP могут содержать некоторые свойства проекта и системы. Эти свойства могут влиять на поведение сборок, поэтому полезно регистрировать их в статистике сборки. Эти свойства могут содержать конфиденциальные данные, например, пароли или полный путь к проекту.
Вы можете отключить сбор этой статистики, добавив свойство kotlin.build.report.http.verbose_environment в вашу gradle.properties.
Что дальше?
Узнайте больше о:
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/gradle-compilation-and-caches.html