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
59
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
100
Improving Android Build Performance
autonomousapps
4
610
Other Decks in Programming
See All in Programming
タイムゾーンの奥地は思ったよりも闇深いかもしれない
suguruooki
1
570
地域ITコミュニティの活性化とAWSに移行してみた話
yuukis
0
230
SQL Server ベクトル検索
odashinsuke
0
170
PHPバージョンアップから始めるOSSコントリビュート / how2oss-contribute
dmnlk
1
990
SEAL - Dive into the sea of search engines - Symfony Live Berlin 2025
alexanderschranz
1
130
Ruby's Line Breaks
yui_knk
2
480
Building a macOS screen saver with Kotlin (Android Makers 2025)
zsmb
1
140
データベースエンジニアの仕事を楽にする。PgAssistantの紹介
nnaka2992
9
4.5k
スモールスタートで始めるためのLambda×モノリス
akihisaikeda
2
180
State of Namespace
tagomoris
4
760
Deoptimization: How YJIT Speeds Up Ruby by Slowing Down / RubyKaigi 2025
k0kubun
0
500
コンテナでLambdaをデプロイするときに知っておきたかったこと
_takahash
0
180
Featured
See All Featured
[RailsConf 2023] Rails as a piece of cake
palkan
54
5.4k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
5
520
Building an army of robots
kneath
304
45k
For a Future-Friendly Web
brad_frost
176
9.7k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
12k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
331
21k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
13
1.4k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
Statistics for Hackers
jakevdp
798
220k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.7k
Code Reviewing Like a Champion
maltzj
522
39k
Gamification - CAS2011
davidbonilla
81
5.2k
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