Slide 1

Slide 1 text

Improving Android Build Performance Tony Robalik, Gradle Inc.

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

The Cost of Builds (#perfmatters)

Slide 4

Slide 4 text

| 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/

Slide 5

Slide 5 text

| X

Slide 6

Slide 6 text

| X

Slide 7

Slide 7 text

| X Fast Builds Matter Don’t even get me started on morale…

Slide 8

Slide 8 text

| X Faster Builds are Possible

Slide 9

Slide 9 text

General Advice

Slide 10

Slide 10 text

| X #science 1. Define scenario to improve 2. Profile scenario 3. Identify biggest bottleneck 4. Fix bottleneck 5. Verify fix by measurement 6. Repeat

Slide 11

Slide 11 text

| 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"] }

Slide 12

Slide 12 text

| 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"] }

Slide 13

Slide 13 text

| 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"] }

Slide 14

Slide 14 text

| X Stay Up-To-Date buildscript { repositories { google() } dependencies { classpath ‘com.android.tools.build:gradle:3.4.2’ } } $ ./gradlew wrapper --gradle-version 5.5.1

Slide 15

Slide 15 text

| X JVM tuning Provide enough heap space Other tweaks often do more harm than good Spend your time on structural improvements

Slide 16

Slide 16 text

| 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

Slide 17

Slide 17 text

| X JVM tuning

Slide 18

Slide 18 text

Diagnosing build issues

Slide 19

Slide 19 text

| X Build Scans

Slide 20

Slide 20 text

| X Build Scans $ ./gradlew assembleDebug --scan

Slide 21

Slide 21 text

| X Build Scans $ ./gradlew assembleDebug --scan

Slide 22

Slide 22 text

| X The Build Lifecycle

Slide 23

Slide 23 text

| X Red Flags Startup/Settings/buildSrc > 5s Single-line change ℳ clean build No-op build doing any work at all High GC time

Slide 24

Slide 24 text

| X Red Flags Startup/Settings/buildSrc > 5s Single-line change ℳ clean build No-op build doing any work at all High GC time

Slide 25

Slide 25 text

| X Red Flags Startup/Settings/buildSrc > 5s Single-line change ℳ clean build No-op build doing any work at all High GC time

Slide 26

Slide 26 text

| X Red Flags Startup/Settings/buildSrc > 5s Single-line change ℳ clean build No-op build doing any work at all High GC time

Slide 27

Slide 27 text

| X Red Flags Startup/Settings/buildSrc > 5s Single-line change ℳ clean build No-op build doing any work at all High GC time

Slide 28

Slide 28 text

Your Build

Slide 29

Slide 29 text

Startup, buildSrc, Settings

Slide 30

Slide 30 text

| X Startup, buildSrc, Settings Use the daemon, keep it healthy

Slide 31

Slide 31 text

| X Startup, buildSrc, Settings Don’t do this // settings.gradle rootDir.listFiles({ File dir, String name -> name.endsWith(“.gradle") } as FilenameFilter) .each { include "$it" } ? ¿

Slide 32

Slide 32 text

| X Startup, buildSrc, Settings Don’t do this // settings.gradle rootDir.listFiles({ File dir, String name -> name.endsWith(“.gradle") } as FilenameFilter) .each { include "$it" }

Slide 33

Slide 33 text

| X Startup, buildSrc, Settings ?

Slide 34

Slide 34 text

Configuration Time

Slide 35

Slide 35 text

| X Configuration Time Applying plugins Evaluating build scripts Running afterEvaluate {} blocks

Slide 36

Slide 36 text

| X Configuration Time When running any task Even gradlew help / gradlew tasks Android Studio sync

Slide 37

Slide 37 text

| X Resolution at Configuration Time

Slide 38

Slide 38 text

| 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” }

Slide 39

Slide 39 text

| 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” }

Slide 40

Slide 40 text

| 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

Slide 41

Slide 41 text

| 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”) ] }

Slide 42

Slide 42 text

| X Inefficient Plugins

Slide 43

Slide 43 text

| 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" }

Slide 44

Slide 44 text

| X Inefficient Plugins // `version.gradle` as before // root `build.gradle` apply from: "$rootDir/version.gradle" subprojects { version = rootProject.version } Reuse expensive calculations

Slide 45

Slide 45 text

| 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 } }

Slide 46

Slide 46 text

| 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 } }

Slide 47

Slide 47 text

| X Configuration Time Avoid dependency resolution Avoid I/O Don’t repeat yourself

Slide 48

Slide 48 text

Optimizing Build Logic

Slide 49

Slide 49 text

| 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

Slide 50

Slide 50 text

| 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

Slide 51

Slide 51 text

| X Extract Binary Plugins https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources $rootDir/buildSrc/ src/main// com/yourCompany/ all/the/things/

Slide 52

Slide 52 text

Execution Time

Slide 53

Slide 53 text

| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable

Slide 54

Slide 54 text

| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable

Slide 55

Slide 55 text

| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable

Slide 56

Slide 56 text

| X Execution Time Executing selected tasks Incremental Cacheable Parallelizable

Slide 57

Slide 57 text

| X Incremental Builds Nothing changed? Executed tasks should be zero!

Slide 58

Slide 58 text

| X Incremental Builds Nothing changed? Executed tasks should be zero!

Slide 59

Slide 59 text

| X Incremental Builds Find volatile inputs

Slide 60

Slide 60 text

| X Incremental Builds android { defaultConfig { versionName “4.0.0-${new Date()}” } }

Slide 61

Slide 61 text

| X Example: Crashlytics Unique IDs are the bane of local dev performance

Slide 62

Slide 62 text

| X Example: Crashlytics Unique IDs are the bane of local dev performance apply plugin: 'io.fabric' android { buildTypes { debug { ext.alwaysUpdateBuildId = false } } }

Slide 63

Slide 63 text

| X Faster Compilation Modularization => Compile avoidance Decoupled code => Faster incremental compilation Careful with Kotlin annotation processing (for now)

Slide 64

Slide 64 text

| 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

Slide 65

Slide 65 text

| X Faster Compilation Modularization => Compile avoidance Decoupled code => Faster incremental compilation Careful with Kotlin annotation processing (for now)

Slide 66

Slide 66 text

| X Faster Compilation Modularization => Compile avoidance Decoupled code => Faster incremental compilation Careful with Kotlin annotation processing (for now)

Slide 67

Slide 67 text

| X Incremental Annotation Processing Since Gradle 4.7 Early adopters: Lombok, Android-State, Dagger Others: github.com/gradle/gradle/issues/5277 (aka incap)

Slide 68

Slide 68 text

| X Incremental Annotation Processing Since Gradle 4.7 Early adopters: Lombok, Android-State, Dagger Others: github.com/gradle/gradle/issues/5277 (aka incap)

Slide 69

Slide 69 text

| X The Build Cache https://guides.gradle.org/using-build-cache/ https://developer.android.com/studio/build/build-cache

Slide 70

Slide 70 text

| X The Build Cache https://guides.gradle.org/using-build-cache/ https://developer.android.com/studio/build/build-cache

Slide 71

Slide 71 text

| 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

Slide 72

Slide 72 text

| 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

Slide 73

Slide 73 text

| X The Build Cache https://guides.gradle.org/using-build-cache/ Many tasks are cacheable https://developer.android.com/studio/build/build-cache

Slide 74

Slide 74 text

| 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

Slide 75

Slide 75 text

| X Parallelism # Or in gradle.properties org.gradle.parallel=true $ ./gradlew assembleDebug --parallel

Slide 76

Slide 76 text

| X Parallelism Serial execution

Slide 77

Slide 77 text

| X Parallelism --parallel and small, decoupled modules

Slide 78

Slide 78 text

Keeping track of performance

Slide 79

Slide 79 text

| X Gradle Enterprise

Slide 80

Slide 80 text

| X See all Builds Find out what colleagues are struggling with

Slide 81

Slide 81 text

| X Detailed Performance Profile

Slide 82

Slide 82 text

| X

Slide 83

Slide 83 text

| X Build Trends

Slide 84

Slide 84 text

| X Build Trends

Slide 85

Slide 85 text

Resources

Slide 86

Slide 86 text

| 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

Slide 87

Slide 87 text

| X scans.gradle.com

Slide 88

Slide 88 text

w Thank you DenverDroids, July 2019 [email protected] @autonomousapps