Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Android Build Performance - DroidconSF 2018
Search
Tony Robalik
November 20, 2018
Programming
1
55
Android Build Performance - DroidconSF 2018
This is a reprisal of my earlier presentation at DroidconNYC, with a few minor updates.
Tony Robalik
November 20, 2018
Tweet
Share
More Decks by Tony Robalik
See All by Tony Robalik
Android Build Performance at DenverDroids
autonomousapps
1
97
Improving Android Build Performance
autonomousapps
4
600
Other Decks in Programming
See All in Programming
Make Impossible States Impossibleを 意識してReactのPropsを設計しよう
ikumatadokoro
0
240
Modular Monolith Monorepo ~シンプルさを保ちながらmonorepoのメリットを最大化する~
yuisakamoto
2
130
Creating a Free Video Ad Network on the Edge
mizoguchicoji
0
120
CSC509 Lecture 13
javiergs
PRO
0
110
シェーダーで魅せるMapLibreの動的ラスタータイル
satoshi7190
1
480
Kaigi on Rails 2024 〜運営の裏側〜
krpk1900
1
240
エンジニアとして関わる要件と仕様(公開用)
murabayashi
0
300
Amazon Bedrock Agentsを用いてアプリ開発してみた!
har1101
0
340
最新TCAキャッチアップ
0si43
0
200
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
CSC509 Lecture 11
javiergs
PRO
0
180
イベント駆動で成長して委員会
happymana
1
340
Featured
See All Featured
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
840
Building an army of robots
kneath
302
43k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Designing for humans not robots
tammielis
250
25k
Automating Front-end Workflow
addyosmani
1366
200k
Agile that works and the tools we love
rasmusluckow
327
21k
The Invisible Side of Design
smashingmag
298
50k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
We Have a Design System, Now What?
morganepeng
50
7.2k
Thoughts on Productivity
jonyablonski
67
4.3k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
246
1.3M
GraphQLとの向き合い方2022年版
quramy
43
13k
Transcript
Improving Android Build Performance Tony Robalik, Gradle Inc.
None
The Cost of Builds (#perfmatters)
| X Fast Builds Matter 60s waste * 50 builds
per day * 10 developers > 8h wasted per day …and that’s not even considering lost focus https://gradle.com/quantifying-the-costs-of-builds/
| X
| X
| X Fast Builds Matter Don’t even get me started
on morale…
General Advice
| X #science 1. Define scenario to improve 2. Profile
scenario 3. Identify biggest bottleneck 4. Fix bottleneck 5. Verify fix by measurement 6. Repeat
| X Automate your measurements github.com/gradle/gradle-profiler $ gradle-profiler --benchmark --scenario-file
scenarios // Scenario 1 configurationTime { tasks = ["help"] } // Scenario 2 assembleNoCache { tasks = ["clean", “assembleDebug”] gradle-args = ["--no-build-cache"] } // Scenario 3 assembleCache { tasks = ["clean", "assembleDebug"] gradle-args = ["--build-cache"] }
| X Automate your measurements github.com/gradle/gradle-profiler $ gradle-profiler --benchmark --scenario-file
scenarios // Scenario 1 configurationTime { tasks = ["help"] } // Scenario 2 assembleNoCache { tasks = ["clean", “assembleDebug”] gradle-args = ["--no-build-cache"] } // Scenario 3 assembleCache { tasks = ["clean", "assembleDebug"] gradle-args = ["--build-cache"] }
| X Automate your measurements github.com/gradle/gradle-profiler $ gradle-profiler --benchmark --scenario-file
scenarios // Scenario 1 configurationTime { tasks = ["help"] } // Scenario 2 assembleNoCache { tasks = ["clean", “assembleDebug”] gradle-args = ["--no-build-cache"] } // Scenario 3 assembleCache { tasks = ["clean", "assembleDebug"] gradle-args = ["--build-cache"] }
| X Stay Up-To-Date buildscript { repositories { google() }
dependencies { classpath ‘com.android.tools.build:gradle:3.2.1’ } } $ ./gradlew wrapper --gradle-version 4.10.2
| X JVM tuning Provide enough heap space Other tweaks
often do more harm than good Spend your time on structural improvements
| X JVM tuning Provide enough heap space Other tweaks
often do more harm than good Spend your time on structural improvements # The only useful argument org.gradle.jvmargs=-Xmx4g
| X JVM tuning
Where is the Problem?
| X Build Scans
| X Build Scans $ ./gradlew assembleDebug --scan
| X Build Scans $ ./gradlew assembleDebug --scan
| X The Build Lifecycle
| X Red Flags Startup/Settings/buildSrc > 1s Single-line change ℳ
clean build No-op build doing any work at all High GC time
| X Red Flags Startup/Settings/buildSrc > 1s Single-line change ℳ
clean build No-op build doing any work at all High GC time
| X Red Flags Startup/Settings/buildSrc > 1s Single-line change ℳ
clean build No-op build doing any work at all High GC time
| X Red Flags Startup/Settings/buildSrc > 1s Single-line change ℳ
clean build No-op build doing any work at all High GC time
| X Red Flags Startup/Settings/buildSrc > 1s Single-line change ℳ
clean build No-op build doing any work at all High GC time
Your Build
Startup, buildSrc, Settings
| X Startup, buildSrc, Settings Use the daemon, keep it
healthy
| X Startup, buildSrc, Settings Don’t do this // settings.gradle
rootDir.listFiles({ File dir, String name -> name.endsWith(“.gradle") } as FilenameFilter) .each { include "$it" } ? ¿
| X Startup, buildSrc, Settings Don’t do this // settings.gradle
rootDir.listFiles({ File dir, String name -> name.endsWith(“.gradle") } as FilenameFilter) .each { include "$it" }
| X Startup, buildSrc, Settings ?
Configuration Time
| X Configuration Time Applying plugins Evaluating build scripts Running
afterEvaluate {} blocks
| X Configuration Time When running any task Even gradlew
help / gradlew tasks Android Studio sync
| X Resolution at Configuration Time
| X Eager resolution Resolution at Configuration Time task badTask(type:
Jar) { from configurations.compile.collect { it.directory ? it : zipTree(it) } classifier “don’t do this” }
| X Eager resolution Resolution at Configuration Time task badTask(type:
Jar) { from configurations.compile.collect { it.directory ? it : zipTree(it) } classifier “don’t do this” }
| X Resolution at Configuration Time task badTask(type: Jar) {
from { configurations.compile.collect { it.directory ? it : zipTree(it) } } classifier “don’t do this” } Use lazy evaluation instead
| X Configuring Tasks import org.jmailen.gradle.kotlinter.tasks.LintTask tasks.register('lintKotlinAll', LintTask) { group
= 'verification' source files(modules.collect { "$it/src" }) reports = [ 'plain': file("$buildDir/reports/ktlint/all-lint.txt"), 'html' : file(“$buildDir/reports/ktlint/all-lint.html”) ] }
| X Inefficient Plugins
| X Expensive logic for each project Inefficient Plugins //
`version.gradle` def out = new ByteArrayOutputStream() exec { commandLine 'git', 'rev-parse', “HEAD” standardOutput = out workingDir = rootDir } version = new String(out.toByteArray()) // root `build.gradle` subprojects { apply from: "$rootDir/version.gradle" }
| X Inefficient Plugins // `version.gradle` as before // root
`build.gradle` apply from: "$rootDir/version.gradle" subprojects { version = rootProject.version } Reuse expensive calculations
| X Variant Explosion https://developer.android.com/studio/build/build-variants#filter-variants variantFilter { variant -> def
flavorName = variant.flavors[0].name def freeFlavor = flavorName == "free" if (!freeFlavor && variant.buildType.name == "release") { variant.ignore = true } }
| X Variant Explosion https://developer.android.com/studio/build/build-variants#filter-variants variantFilter { variant -> def
flavorName = variant.flavors[0].name def freeFlavor = flavorName == "free" if (!freeFlavor && variant.buildType.name == "release") { variant.ignore = true } }
| X Configuration Time Avoid dependency resolution Avoid I/O Don’t
repeat yourself
Optimizing Build Logic
| X Extract Script Plugins apply from: "$rootDir/static_analysis.gradle" apply from:
"$rootDir/coverage.gradle" apply from: “$rootDir/spock_android_lib.gradle” apply from: "$rootDir/junit5html.gradle" if (isOnCi()) { // This plugin adds ~5s to every build apply plugin: 'com.getkeepsafe.dexcount' } Makes finding issues easier
| X Extract Binary Plugins Use buildSrc Use static compilation
Keep build scripts declarative https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources
| X Extract Binary Plugins https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources $rootDir/buildSrc/ src/main/<java|kotlin|groovy>/ com/yourCompany/ all/the/things/
Execution Time
| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable
| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable
| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable
| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable
| X Incremental Builds Nothing changed? Executed tasks should be
zero!
| X Incremental Builds Nothing changed? Executed tasks should be
zero!
| X Incremental Builds Find volatile inputs
| X Incremental Builds android { defaultConfig { versionName “4.0.0-${new
Date()}” } }
| X Example: Crashlytics Unique IDs are the bane of
local dev performance
| X Example: Crashlytics Unique IDs are the bane of
local dev performance apply plugin: 'io.fabric' android { buildTypes { debug { ext.alwaysUpdateBuildId = false } } }
| X Faster Compilation Modularization => Compile avoidance Decoupled code
=> Faster incremental compilation Careful with Kotlin annotation processing (for now)
| X Faster Compilation Modularization => Compile avoidance Decoupled code
=> Faster incremental compilation Careful with Kotlin annotation processing (for now) https://youtrack.jetbrains.com/issue/KT-24203
| X Faster Compilation Modularization => Compile avoidance Decoupled code
=> Faster incremental compilation Careful with Kotlin annotation processing (for now)
| X Faster Compilation Modularization => Compile avoidance Decoupled code
=> Faster incremental compilation Careful with Kotlin annotation processing (for now)
| X Incremental Annotation Processing Since Gradle 4.7 Early adopters:
Lombok, Android-State, Dagger Others: github.com/gradle/gradle/issues/5277 (incap)
| X Incremental Annotation Processing Since Gradle 4.7 Early adopters:
Lombok, Android-State, Dagger Others: github.com/gradle/gradle/issues/5277 (incap)
| X The Build Cache https://guides.gradle.org/using-build-cache/ https://developer.android.com/studio/build/build-cache
| X The Build Cache https://guides.gradle.org/using-build-cache/ https://developer.android.com/studio/build/build-cache
| X The Build Cache https://guides.gradle.org/using-build-cache/ # gradle.properties org.gradle.caching=true https://developer.android.com/studio/build/build-cache
| X The Build Cache https://guides.gradle.org/using-build-cache/ # gradle.properties org.gradle.caching=true $
./gradlew assembleDebug --build-cache https://developer.android.com/studio/build/build-cache
| X The Build Cache https://guides.gradle.org/using-build-cache/ Many tasks are cacheable
https://developer.android.com/studio/build/build-cache
| X https://developer.android.com/studio/build/build-cache Save time for your whole team with
one or more remote caches https://guides.gradle.org/using-build-cache/ The Build Cache
| X https://developer.android.com/studio/build/build-cache https://guides.gradle.org/using-build-cache/ The Build Cache // root build.gradle
subprojects { pluginManager.withPlugin("kotlin-kapt") { kapt.useBuildCache = true } }
| X Parallelism # Or in gradle.properties org.gradle.parallel=true $ ./gradlew
assembleDebug --parallel
| X Parallelism Serial execution
| X Parallelism --parallel and small, decoupled projects
Keeping track of performance
| X Gradle Enterprise
| X See all Builds Find out what colleagues are
struggling with
| X Detailed Performance Profile
| X Watch Performance over Time
| X Watch Performance over Time
| X
Resources
| X Gradle performance guide https://guides.gradle.org/performance Android performance guide https://developer.android.com/studio/build/optimize-your-build.html
Plugin development guide https://guides.gradle.org/implementing-gradle-plugins Structuring build logic guide https://docs.gradle.org/current/userguide/organizing_build_logic.html Guides
| X scans.gradle.com
w Thank you DroidconSF 2018
[email protected]
@autonomousapps