Android编译优化~Gradle构建基准测试

背景

之前对安卓打包编译优化有所实践,但当时对优化提升结果采取了手动测试的办法才拿到结果,而且遇到大型工程更是痛不欲生。不过当时采取的策略是将增量测试代码提到了Git,编译一次抄一次代码,样本数据只重复了10次,中间不能出现错误操作不然样本数据参考意义不大。当时不清楚有更好的测试方案,但后续随着了解的深入,谷歌官方有介绍如何对Gradle构建过程进行基准测试。

基本使用

安装gradle-profiler

这块GitHub有说明,但实操后还是建议用MAC来做。如果是Windows还需要安装SDKMAN和子系统。
MAC命令行切到我们的项目根目录,执行会自动根据我们工程配置下载Gradle

brew install gradle-profiler
gradle-profiler --benchmark help


* Writing results to /Users/hynson/StudioProjects/SimpleGallery/profile-out

* Settings
Project dir: /Users/hynson/StudioProjects/SimpleGallery
Output dir: /Users/hynson/StudioProjects/SimpleGallery/profile-out
Profiler: none
Benchmark: true
Versions: []
Gradle User Home: /Users/hynson/StudioProjects/SimpleGallery/gradle-user-home
Targets: [help]

* Inspecting the build using its default Gradle version
Downloading https://services.gradle.org/distributions/gradle-7.3.3-bin.zip
..............................................................................................................
* Stopping daemons

* Scenarios
Scenario: using Gradle 7.3.3
  Gradle 7.3.3 (/Users/hynson/StudioProjects/SimpleGallery/gradle-user-home/wrapper/dists/gradle-7.3.3-bin/6a41zxkdtcxs8rphpq6y0069z/gradle-7.3.3)
  Run using: Tooling API
  Run: run tasks help
  Cleanup: do nothing
  Gradle args: []
  Build changes: []
  Warm-ups: 6
  Builds: 10

* Running scenario using Gradle 7.3.3 (scenario 1/1)
编写测试场景文件

在项目根目录新建scenarios.txt文件,参考文档编写JSON格式的场景命令。如

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

关于如何写场景命令,更详细的使用说明,GitHub有更详细的指导。

执行测试用例

执行下面命令后,命令行运行效果

gradle-profiler --benchmark --project-dir ./ --scenario-file scenarios.txt

运行测试用例执行完后,到这一步我们项目目录发生了如下变化,profile.log是我们Gradle执行的编译日志,CSV则是数据,html就是我们的测试报告了。
在这里插入图片描述

分析测试结果

用浏览器打开benchmark.html报告
测试结果以上是Gradle --max-workers=1和Gradle --max-workers=4两次的测试结果对比。

增量Build分析(Profiling incremental builds)

增量是我们进行性能分析最重要的场景,Gradle支持以下自动更改操作,它执行修改完成后会恢复原始代码。

  • 修改方法征文、添加新方法
  • 修改布局和字符串资源
incremental_build {
    tasks = ["assemble"]

    apply-build-script-change-to = "build.gradle.kts"
    apply-project-dependency-change-to {
        files = ["build.gradle"]
        # Default number of dependency-count is 3.
        # Gradle Profiler will simulate changes to project dependencies by generate some additional projects and then add a combination of project dependencies to every non-generated subprojects before each iteration.
        # The profiler will generate the minimal number of subprojects to allow for a unique combination of dependencies to be used for each iteration.
        # Note: Number of generated projects is calculated as binomial coffiecient: "from `x` choose `dependency-count` = `iterations * files`", where number of generated projects is `x`.
        dependency-count = 3
    }
    apply-abi-change-to = "src/main/java/MyThing.java"
    apply-non-abi-change-to = ["src/main/java/MyThing.java", "src/main/java/MyOtherThing.java"]
    apply-h-change-to = "src/main/headers/app.h"
    apply-cpp-change-to = "src/main/cpp/app.cpp"
    apply-property-resource-change-to = "src/main/resources/thing.properties"
    apply-android-resource-change-to = "src/main/res/values/strings.xml"
    apply-android-resource-value-change-to = "src/main/res/values/strings.xml"
    apply-android-manifest-change-to = "src/main/AndroidManifest.xml"
    clear-build-cache-before = SCENARIO
    clear-transform-cache-before = BUILD
    show-build-cache-size = true
    git-checkout = {
        cleanup = "efb43a1"
        build = "master"
    }
    git-revert = ["efb43a1"]
    jvm-args = ["-Xmx2500m", "-XX:MaxMetaspaceSize=512m"]
}

这里需要注意,场景文件的任务执行顺序是自下而上的,还有更高级的用法值得深入研究。

androidStudioSync

这里附上一个用到的一个场景供参考,有时我们需要执行AS的Sync(比如我们修改了local.properties文件),我们就需要在场景文件中新增

androidStudioSync {
title = “Android Studio Sync”
# Measure an Android studio sync
# Note: Android Studio Bumblebee (2021.1.1) or newer is required
android-studio-sync {
# Override default Android Studio jvm args
# studio-jvm-args = [“-Xms256m”, “-Xmx4096m”]
}
}

然后在命令行执行需要指定AS的安装目录,然后它会自动打开AS执行同样的操作10次后自动关闭AS,这个还是比较耗时,建议非必要测试场景不要配置AS同步。

gradle-profiler --benchmark --project-dir ./ --scenario-file scenarios_add.txt

* Writing results to /Users/hynson/StudioProjects/SimpleGallery/profile-out-11

* Settings
Project dir: /Users/hynson/StudioProjects/SimpleGallery/.
Output dir: /Users/hynson/StudioProjects/SimpleGallery/profile-out-11
Profiler: none
Benchmark: true
Versions: []
Gradle User Home: /Users/hynson/StudioProjects/SimpleGallery/gradle-user-home
Targets: []

* Inspecting the build using its default Gradle version

* Stopping daemons
java.lang.IllegalArgumentException: Android Studio installation directory should be specified using --studio-install-dir when measuring Android studio sync.
	at org.gradle.profiler.ScenarioLoader.getBuildAction(ScenarioLoader.java:474)
	at org.gradle.profiler.ScenarioLoader.loadScenarios(ScenarioLoader.java:270)
	at org.gradle.profiler.ScenarioLoader.doLoadScenarios(ScenarioLoader.java:175)
	at org.gradle.profiler.ScenarioLoader.loadScenarios(ScenarioLoader.java:156)
	at org.gradle.profiler.Main.run(Main.java:56)
	at org.gradle.profiler.Main.main(Main.java:25)

hynson@housaibangdeiMac SimpleGallery % gradle-profiler --benchmark --studio-install-dir "/Applications/Android Studio.app/" --project-dir ./ --scenario-file scenarios_add.txt 

* Writing results to /Users/hynson/StudioProjects/SimpleGallery/profile-out-20

* Settings
Project dir: /Users/hynson/StudioProjects/SimpleGallery/.
Output dir: /Users/hynson/StudioProjects/SimpleGallery/profile-out-20
Profiler: none
Benchmark: true
Versions: []
Gradle User Home: /Users/hynson/StudioProjects/SimpleGallery/gradle-user-home
Targets: []

* Inspecting the build using its default Gradle version

* Stopping daemons

* Scenarios
Scenario: Android Studio Sync using Gradle 7.3.3
  Gradle 7.3.3 (/Users/hynson/StudioProjects/SimpleGallery/gradle-user-home/wrapper/dists/gradle-7.3.3-bin/6a41zxkdtcxs8rphpq6y0069z/gradle-7.3.3)
  Run using: Android Studio
  Run: Android Studio sync
  Cleanup: do nothing
  Gradle args: []
  Build changes: []
  Warm-ups: 6
  Builds: 10
Scenario: string_resource_change using Gradle 7.3.3
  Gradle 7.3.3 (/Users/hynson/StudioProjects/SimpleGallery/gradle-user-home/wrapper/dists/gradle-7.3.3-bin/6a41zxkdtcxs8rphpq6y0069z/gradle-7.3.3)
  Run using: Tooling API
  Run: run tasks :app_kt:assembleDebug
  Cleanup: do nothing
  Gradle args: []
  Build changes: [ApplyValueChangeToAndroidResourceFileMutator(/Users/hynson/StudioProjects/SimpleGallery/./app_kt/src/main/res/values/strings.xml)]
  Warm-ups: 6
  Builds: 10

* Running scenario Android Studio Sync using Gradle 7.3.3 (scenario 1/2)

* Stopping daemons

* Running warm-up build #1


* Starting Android Studio at /Applications/Android Studio.app
* Java command: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
* Classpath:
  /Applications/Android Studio.app/Contents/lib/util.jar
  /Applications/Android Studio.app/Contents/lib/bootstrap.jar
* System properties:
  gradle.profiler.startup.port -> 59499
  gradle.profiler.port -> 59500
  idea.config.path -> /Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/config
  idea.log.path -> /Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/logs
  idea.jre.check -> true
  idea.system.path -> /Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/system
  idea.paths.selector -> AndroidStudio2021.3
  idea.vendor.name -> Google
  java.system.class.loader -> com.intellij.util.lang.PathClassLoader
  idea.gradle.distributionType -> BUNDLED
  idea.plugins.path -> /Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/plugins
  idea.trust.all.projects -> true
  splash -> true
  idea.platform.prefix -> AndroidStudio
  idea.executable -> studio
  idea.home.path -> /Applications/Android Studio.app/Contents
* Main class: com.intellij.idea.Main
* Android Studio logs can be found at: /Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/logs/idea.log
* Using command line: [/Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java, -cp, /Applications/Android Studio.app/Contents/lib/util.jar:/Applications/Android Studio.app/Contents/lib/bootstrap.jar, -Dgradle.profiler.startup.port=59499, -Dgradle.profiler.port=59500, -Didea.config.path=/Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/config, -Didea.log.path=/Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/logs, -Didea.jre.check=true, -Didea.system.path=/Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/system, -Didea.paths.selector=AndroidStudio2021.3, -Didea.vendor.name=Google, -Djava.system.class.loader=com.intellij.util.lang.PathClassLoader, -Didea.gradle.distributionType=BUNDLED, -Didea.plugins.path=/Users/hynson/StudioProjects/SimpleGallery/profile-out-20/studio-sandbox/plugins, -Didea.trust.all.projects=true, -Dsplash=true, -Didea.platform.prefix=AndroidStudio, -Didea.executable=studio, -Didea.home.path=/Applications/Android Studio.app/Contents, --add-opens=java.base/java.io=ALL-UNNAMED, --add-opens=java.base/java.lang=ALL-UNNAMED, --add-opens=java.base/java.lang.reflect=ALL-UNNAMED, --add-opens=java.base/java.net=ALL-UNNAMED, --add-opens=java.base/java.nio=ALL-UNNAMED, --add-opens=java.base/java.nio.charset=ALL-UNNAMED, --add-opens=java.base/java.text=ALL-UNNAMED, --add-opens=java.base/java.time=ALL-UNNAMED, --add-opens=java.base/java.util=ALL-UNNAMED, --add-opens=java.base/java.util.concurrent=ALL-UNNAMED, --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED, --add-opens=java.base/jdk.internal.vm=ALL-UNNAMED, --add-opens=java.base/sun.nio.ch=ALL-UNNAMED, --add-opens=java.base/sun.security.ssl=ALL-UNNAMED, --add-opens=java.base/sun.security.util=ALL-UNNAMED, --add-opens=java.desktop/com.sun.java.swing.plaf.gtk=ALL-UNNAMED, --add-opens=java.desktop/java.awt=ALL-UNNAMED, --add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED, --add-opens=java.desktop/java.awt.event=ALL-UNNAMED, --add-opens=java.desktop/java.awt.image=ALL-UNNAMED, --add-opens=java.desktop/java.awt.peer=ALL-UNNAMED, --add-opens=java.desktop/javax.swing=ALL-UNNAMED, --add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED, --add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED, --add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED, --add-opens=java.desktop/sun.awt.datatransfer=ALL-UNNAMED, --add-opens=java.desktop/sun.awt.image=ALL-UNNAMED, --add-opens=java.desktop/sun.awt=ALL-UNNAMED, --add-opens=java.desktop/sun.font=ALL-UNNAMED, --add-opens=java.desktop/sun.java2d=ALL-UNNAMED, --add-opens=java.desktop/sun.swing=ALL-UNNAMED, --add-opens=jdk.attach/sun.tools.attach=ALL-UNNAMED, --add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED, --add-opens=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED, --add-opens=jdk.jdi/com.sun.tools.jdi=ALL-UNNAMED, --add-exports=java.desktop/com.apple.eawt=ALL-UNNAMED, --add-exports=java.desktop/com.apple.laf=ALL-UNNAMED, --add-exports=java.desktop/com.apple.eawt.event=ALL-UNNAMED, -javaagent:/private/var/folders/93/fk0382_90qbcctczf5s2xhkr0000gn/T/studio-agent13826845936541378830.jar=59501,/private/var/folders/93/fk0382_90qbcctczf5s2xhkr0000gn/T/instrumentation-support8915076811485037397.jar, --add-exports, java.base/jdk.internal.misc=ALL-UNNAMED, -Xbootclasspath/a:/private/var/folders/93/fk0382_90qbcctczf5s2xhkr0000gn/T/asm14122579247830138713.jar:/private/var/folders/93/fk0382_90qbcctczf5s2xhkr0000gn/T/client-protocol15407110553657043473.jar, -Xms256m, -Xmx4096m, com.intellij.idea.Main, /Users/hynson/StudioProjects/SimpleGallery/.]

* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 441762ms
* Full Gradle execution time: 441762ms
* Full IDE execution time: 398563ms
* Full sync has completed in: 840325ms and it SUCCEEDED
Execution time 840325 ms

* Running warm-up build #2
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 22032ms
* Full Gradle execution time: 22032ms
* Full IDE execution time: 2025ms
* Full sync has completed in: 24057ms and it SUCCEEDED
Execution time 24057 ms

* Running warm-up build #3
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 3824ms
* Full Gradle execution time: 3824ms
* Full IDE execution time: 1073ms
* Full sync has completed in: 4897ms and it SUCCEEDED
Execution time 4897 ms

* Running warm-up build #4
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 1175ms
* Full Gradle execution time: 1175ms
* Full IDE execution time: 427ms
* Full sync has completed in: 1602ms and it SUCCEEDED
Execution time 1602 ms

* Running warm-up build #5
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 964ms
* Full Gradle execution time: 964ms
* Full IDE execution time: 394ms
* Full sync has completed in: 1358ms and it SUCCEEDED
Execution time 1358 ms

* Running warm-up build #6
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 1014ms
* Full Gradle execution time: 1014ms
* Full IDE execution time: 301ms
* Full sync has completed in: 1315ms and it SUCCEEDED
Execution time 1315 ms

* Running measured build #1
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 1227ms
* Full Gradle execution time: 1227ms
* Full IDE execution time: 282ms
* Full sync has completed in: 1509ms and it SUCCEEDED
Execution time 1509 ms

* Running measured build #2
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 1453ms
* Full Gradle execution time: 1453ms
* Full IDE execution time: 350ms
* Full sync has completed in: 1803ms and it SUCCEEDED
Execution time 1803 ms

* Running measured build #3
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 883ms
* Full Gradle execution time: 883ms
* Full IDE execution time: 353ms
* Full sync has completed in: 1236ms and it SUCCEEDED
Execution time 1236 ms

* Running measured build #4
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 997ms
* Full Gradle execution time: 997ms
* Full IDE execution time: 370ms
* Full sync has completed in: 1367ms and it SUCCEEDED
Execution time 1367 ms

* Running measured build #5
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 923ms
* Full Gradle execution time: 923ms
* Full IDE execution time: 320ms
* Full sync has completed in: 1243ms and it SUCCEEDED
Execution time 1243 ms

* Running measured build #6
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 1386ms
* Full Gradle execution time: 1386ms
* Full IDE execution time: 320ms
* Full sync has completed in: 1706ms and it SUCCEEDED
Execution time 1706 ms

* Running measured build #7
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 934ms
* Full Gradle execution time: 934ms
* Full IDE execution time: 3469ms
* Full sync has completed in: 4403ms and it SUCCEEDED
Execution time 4403 ms

* Running measured build #8
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 755ms
* Full Gradle execution time: 755ms
* Full IDE execution time: 331ms
* Full sync has completed in: 1086ms and it SUCCEEDED
Execution time 1086 ms

* Running measured build #9
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 702ms
* Full Gradle execution time: 702ms
* Full IDE execution time: 299ms
* Full sync has completed in: 1001ms and it SUCCEEDED
Execution time 1001 ms

* Running measured build #10
* Running sync in Android Studio...
* Sent sync request
* Sync has started, waiting for it to complete...
* Gradle invocation 1 has started, waiting for it to complete...
* Gradle invocation 1 has completed in: 723ms
* Full Gradle execution time: 723ms
* Full IDE execution time: 340ms
* Full sync has completed in: 1063ms and it SUCCEEDED
Execution time 1063 ms
* Stopping Android Studio....
* Android Studio stopped.

* Stopping daemons

相关文章介绍较少,看了之后也有点一头雾水。如果对你有帮忙,欢迎点赞和关注!

参考:
https://developer.android.google.cn/studio/build/profile-your-build
https://github.com/gradle/gradle-profiler

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会画板子的物联网工程师

如果文章还不错,欢迎点赞收藏~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值