Профилируйте свою сборку

Более крупные проекты или те, которые реализуют много пользовательской логики сборки, могут потребовать от вас более глубокого изучения процесса сборки, чтобы найти узкие места. Вы можете сделать это, профилируя, сколько времени Gradle требуется для выполнения каждой фазы жизненного цикла сборки и каждой задачи сборки. Например, если ваш профиль сборки показывает, что Gradle тратит слишком много времени на настройку вашего проекта, это может указывать на необходимость перенести пользовательскую логику сборки из фазы настройки . Кроме того, если задача mergeDevDebugResources занимает много времени сборки, это может указывать на то, что вам необходимо либо преобразовать изображения в WebP , либо отключить сжатие PNG .

Если вы используете Android Studio 4.0 или более поздней версии, лучший способ исследовать проблемы с производительностью сборки — использовать анализатор сборки .

Кроме того, есть два варианта профилирования вашей сборки за пределами Android Studio:

  1. Автономный инструмент gradle-profiler — надежный инструмент для глубокого анализа вашей сборки.

  2. Опция Gradle --profile — удобный инструмент, доступный из командной строки Gradle.

Использование автономного инструмента gradle-profiler

Чтобы найти настройку проекта, обеспечивающую наилучшую скорость сборки, вам следует использовать профилировщик Gradle , инструмент для сбора информации о профилировании и сравнительном тестировании сборок Gradle. Профилировщик Gradle позволяет создавать сценарии сборки и запускать их несколько раз, предотвращая высокую разницу между результатами и обеспечивая воспроизводимость результатов.

Режим бенчмаркинга следует использовать для сбора информации о чистых и инкрементных сборках, а режим профилирования можно использовать для сбора более подробной информации о запусках, включая снимки ЦП.

Некоторые из конфигураций настройки проекта для сравнительного анализа включают в себя:

  • Версии плагина
  • Версии Gradle
  • Настройки JVM (размер кучи, размер пермгена, сборка мусора и т. д.)
  • Количество рабочих Gradle ( org.gradle.workers.max )
  • Опции для каждого плагина для дальнейшей оптимизации производительности

Начиная

  • Установите gradle-profiler, следуя этим инструкциям.
  • Запустите: gradle-profiler --benchmark --project-dir <root-project> :app:assembleDebug

Это позволит протестировать полностью обновленную сборку, поскольку --benchmark запускает задачу несколько раз, не меняя проект между ними. Затем он сгенерирует HTML-отчет в каталоге profile-out/ показывающий время сборки.

Есть и другие сценарии, которые могут быть более полезны для сравнения:

  • Изменения кода в теле метода в классе, где вы выполняете большую часть своей работы.
  • Изменения API в модуле, который используется в вашем проекте. Хотя это происходит реже, чем изменения в вашем собственном коде, это оказывает большее влияние, и его полезно измерить.
  • Изменения макета для имитации итерации работы пользовательского интерфейса.
  • Редактирование строк для имитации работы по переводу.
  • Чистые сборки для имитации изменений в самой сборке (например, обновление плагина Android Gradle, обновление Gradle или редактирование собственного кода сборки в buildSrc ).

Чтобы протестировать эти варианты использования, вы можете создать сценарий, который будет использоваться для управления выполнением gradle-profiler и который применяет соответствующие изменения к вашим источникам. Ниже вы можете изучить некоторые распространенные сценарии.

Профилирование различных настроек памяти/процессора

Чтобы протестировать различные настройки памяти и процессора, вы можете создать несколько сценариев, в которых используются разные значения для org.gradle.jvmargs . Например, вы можете создавать сценарии:

# <root-project>/scenarios.txt
clean_build_2gb_4workers {
    tasks = [":app:assembleDebug"]
    gradle-args = ["--max-workers=4"]
    jvm-args = ["-Xmx2048m"]
    cleanup-tasks = ["clean"]
}
clean_build_parallelGC {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-XX:+UseParallelGC"]
    cleanup-tasks = ["clean"]
}

clean_build_G1GC_4gb {
    tasks = [":app:assembleDebug"]
    jvm-args = ["-Xmx4096m", "-XX:+UseG1GC"]
    cleanup-tasks = ["clean"]
}

Запуск gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt запустит три сценария, и вы сможете сравнить, сколько времени :app:assembleDebug занимает для каждой из этих настроек. .

Профилирование различных версий плагина Gradle

Чтобы узнать, как изменение версии плагина Gradle влияет на время сборки, создайте сценарий для его сравнительного анализа. Это требует некоторой подготовки, чтобы версию плагина можно было внедрить из сценария. Измените корневой файл build.gradle:

# <root-project>/build.gradle
buildscript {
    def agpVersion = providers.systemProperty("agpVersion").forUseAtConfigurationTime().orNull ?: '4.1.0'

    ext.kotlin = providers.systemProperty('kotlinVersion').forUseAtConfigurationTime().orNull ?: '1.4.0'

    dependencies {
        classpath "com.android.tools.build:gradle:$agpVersion"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin"
    }
}

Теперь вы можете указать версии плагина Android Gradle и плагина Kotlin Gradle из файла сценариев, и сценарий добавит новый метод в исходные файлы:

# <root-project>/scenarios.txt
non_abi_change_agp4.1.0_kotlin1.4.10 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.1.0"
      "kotlinVersion" = "1.4.10"
}

non_abi_change_agp4.2.0_kotlin1.4.20 {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    System-properties {
      "agpVersion" = "4.2.0-alpha16"
      "kotlinVersion" = "1.4.20"
}

Профилирование инкрементной сборки

Большинство сборок являются инкрементными, что делает этот сценарий одним из наиболее важных для профилирования. Профилировщик Gradle имеет обширную поддержку для профилирования инкрементальных сборок . Он может автоматически применять изменения к исходному файлу, изменяя тело метода, добавляя новый метод или изменяя макет или строковый ресурс. Например, вы можете создать такие дополнительные сценарии:

# <root-project>/scenarios.txt
non_abi_change {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

abi_change {
    tasks = [":app:assembleDebug"]
    apply-abi-change-to = ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
}

layout_change {
    tasks = [":app:assembleDebug"]
    apply-android-layout-change-to = "app/src/main/res/your_layout_file.xml"
}
string_resource_change {
    tasks = [":app:assembleDebug"]
    apply-android-resource-value-change-to = "app/src/main/res/values/strings.xml"
}

Запуск gradle-profiler --benchmark --project-dir &lt;root-project> --scenario-file scenarios.txt создаст HTML-отчет с данными сравнительного тестирования.

Вы можете комбинировать инкрементные сценарии с другими настройками, такими как размер кучи, количество рабочих или версия Gradle:

# <root-project>/scenarios.txt
non_abi_change_4g {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
}

non_abi_change_4g_8workers {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx4096m"]
    gradle-args = ["--max-workers=8"]
}

non_abi_change_3g_gradle67 {
    tasks = [":app:assembleDebug"]
    apply-non-abi-change-to ["app/src/main/java/com/example/your_app/your_code_file.java,
                              "app/src/main/java/com/example/your_app/your_code_file.kt"]
    jvm-args = ["-Xmx3072m"]
    version = ["6.7"]
}

Профилирование чистой сборки

Чтобы протестировать чистую сборку, вы можете создать сценарий, который будет использоваться для управления выполнением gradle-profiler:

# <root-project>/scenarios.txt
clean_build {
    tasks = [":app:assembleDebug"]
    cleanup-tasks = ["clean"]
}

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

gradle-profiler --benchmark --project-dir <root-project> --scenario-file scenarios.txt

Использование опции Gradle --profile

Чтобы сгенерировать и просмотреть профиль сборки из командной строки Gradle, выполните следующие шаги:

  1. Откройте терминал командной строки в корне вашего проекта.
  2. Выполните чистую сборку, введя следующую команду. При профилировании сборки вам следует выполнять чистую сборку между каждой профилируемой сборкой, поскольку Gradle пропускает задачи, когда входные данные для задачи (например, исходный код) не изменяются. Таким образом, вторая сборка без изменений входных данных всегда выполняется быстрее, поскольку задачи не запускаются повторно. Таким образом, выполнение задачи clean между сборками гарантирует, что вы профилируете весь процесс сборки.
    // On Mac or Linux, run the Gradle wrapper using "./gradlew".
    gradlew clean
    
  3. Выполните отладочную сборку одного из вариантов вашего продукта, например варианта «dev», со следующими флагами:
    gradlew --profile --offline --rerun-tasks assembleFlavorDebug
    
    • --profile : включает профилирование.
    • --offline : отключает Gradle получение онлайн-зависимостей. Это гарантирует, что любые задержки, вызванные попыткой Gradle обновить ваши зависимости, не повлияют на ваши данные профилирования. Вы должны были уже однажды собрать свой проект, чтобы убедиться, что Gradle уже загрузил и кэшировал ваши зависимости.
    • --rerun-tasks : заставляет Gradle перезапустить все задачи и игнорировать любые оптимизации задач.
  4. Рисунок 1. Вид проекта с указанием расположения профильных отчетов.

    После завершения сборки в окне «Проект» перейдите в project-root /build/reports/profile/ (как показано на рисунке 1).

  5. Щелкните правой кнопкой мыши файл profile- timestamp .html и выберите «Открыть в браузере» > «По умолчанию» . Отчет должен выглядеть примерно так, как показано на рисунке 2. Вы можете просмотреть каждую вкладку отчета, чтобы узнать о своей сборке, например вкладку «Выполнение задачи» , на которой показано, сколько времени потребовалось Gradle для выполнения каждой задачи сборки.

    Рисунок 2. Просмотр отчета в браузере.

  6. Необязательно: прежде чем вносить какие-либо изменения в проект или конфигурацию сборки, повторите команду на шаге 3, но опустите флаг --rerun-tasks . Поскольку Gradle пытается сэкономить время, не выполняя повторно задачи, входные данные которых не изменились (они обозначены как UP-TO-DATE на вкладке «Выполнение задач » отчета, как показано на рис. 3), вы можете определить, какие задачи являются актуальными. выполнение работы, когда этого делать не следует. Например, если :app:processDevUniversalDebugManifest не помечен как UP-TO-DATE , это может означать, что ваша конфигурация сборки динамически обновляет манифест при каждой сборке. Однако некоторые задачи необходимо выполнять во время каждой сборки, например :app:checkDevDebugManifest .

    Рисунок 3. Просмотр результатов выполнения задачи.

Теперь, когда у вас есть отчет о профиле сборки, вы можете начать поиск возможностей оптимизации, проверяя информацию на каждой вкладке отчета. Некоторые настройки сборки требуют экспериментирования, поскольку преимущества могут различаться в зависимости от проекта и рабочей станции. Например, проекты с большой кодовой базой могут выиграть от сжатия кода , чтобы удалить неиспользуемый код и уменьшить размер приложения. Однако небольшие проекты могут получить больше пользы от полного отключения сжатия кода. Кроме того, увеличение размера кучи Gradle (с использованием org.gradle.jvmargs ) может отрицательно повлиять на производительность на машинах с низким объемом памяти.

После внесения изменений в конфигурацию сборки наблюдайте за результатами изменений, повторяя описанные выше шаги и создавая новый профиль сборки. Например, на рис. 4 показан отчет для того же примера приложения после применения некоторых базовых оптимизаций, описанных на этой странице.

Рисунок 4. Просмотр нового отчета после оптимизации скорости сборки.