Save 37% off PRO during our Black Friday Sale! »

Build高速化の話

8766b19fe1c1f8475e233c910ed6440f?s=47 kobito-kaba
November 20, 2017

 Build高速化の話

.droidconSFのJared Burrowsによる、"Make Your Build Great Again"を試してみたら、ビルド時間が半分になりましたので、まとめました

8766b19fe1c1f8475e233c910ed6440f?s=128

kobito-kaba

November 20, 2017
Tweet

Transcript

  1. Build高 化 お話 Yahoo! JAPAN 森 洋之

  2. None
  3. Make Your Build Great Again!

  4. Jared Burrows Software Engineer @Microsoft Yammer for Android

  5. Before After Reduction Time(min) 〜18+ 〜11 38.89%

  6. ビルド 度 ために 最適化する対象

  7. • ソフトウェア ◦ Gradle ◦ Android Gradle Plugin ◦ Android

    Studio / IntelliJ • ハードウェア ◦ CPU ◦ メモリ
  8. None
  9. $4,999〜

  10. 探るべきも • 遅くなっている原因 • ボトルネックになっているタスク • 不要な依存関係・プラグイン • モジュール/build.gradle 構

  11. スタート地点 • 一番ベースとなっているところから手を付けていく

  12. スタート地点 • 一番ベースとなっているところから手を付けていく • PCを変える、ビルドエージェントを見直すなど

  13. スタート地点 • 一番ベースとなっているところから手を付けていく • PCを変える、ビルドエージェントを見直すなど • Gradle 設定を最適化する

  14. スタート地点 • 一番ベースとなっているところから手を付けていく • PCを変える、ビルドエージェントを見直すなど • Gradle 設定を最適化する • Android

    Gradle Plugin 設定を最適化する
  15. スタート地点 • 一番ベースとなっているところから手を付けていく • PCを変える、ビルドエージェントを見直すなど • Gradle 設定を最適化する • Android

    Gradle Plugin 設定を最適化する • Android Studio 設定を最適化する
  16. Gradle 設定

  17. 基本

  18. ソフトウェアを最新にする • Gradle 4.3を使う

  19. None
  20. ソフトウェアを最新にする • Gradle 4.3を使う • マイナーバージョン間 互換性を維持する

  21. ソフトウェアを最新にする • Gradle 4.3を使う • マイナーバージョン間 互換性を維持する • JVMも最新 も

    を使う
  22. gradle.properties

  23. Gradle daemonを有効にする • Gradleを実行するたびに、新しいインスタンスを 立ち上げている • デーモンを使用することで、ビルドごとにJVMを起動す るコストを避けられる • 3.0〜

    デフォルトで有効
  24. gradle.properties org.gradle.daemon=true

  25. daemon ヒープサイズを増やす • デフォルトで 1GB • Androidプロジェクト 、通常もっと使う • AGP

    2.1以上だと、”Dex in Process”に 最低2GB 必要 • 大きなプロジェクトほど、メモリ 増設が効く
  26. gradle.properties org.gradle.daemon=true

  27. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m

  28. 並列ビルドを有効にする • Gradleタスク 並列実行 ビルド 度を くする • モジュール分割されたプロジェクトで効果的 •

    JavaCompileタスクを別プロセスで行うと良い • テストタスクも別プロセスが良い
  29. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m

  30. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true

  31. Javaコンパイルタスクを分離する tasks.withType(JavaCompile) { options.fork = true }

  32. テストタスクを分離する tasks.withType(Test) { def maxCount = gradle.startParameter.maxWorkerCount maxParallelForks = (maxCount

    < 2) ? 1 : maxCount / 2 forkEvery = 100 }
  33. None
  34. Configure on Demandを有効にする • 関係 あるモジュール/タスクで み Configurationを行う • モジュール分割されたプロジェクトで効果的

    • こ 設定をしても、“subprojects”や”allprojects”で 設定を使うと無意味になる
  35. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true

  36. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true

  37. ビルドキャッシュを有効にする • インクリメンタルビルドによって、 無駄な再ビルドを減らすことができる • ブランチを変えたときにも効果的 • 異なるビルドフレーバーをビルドする時に効果的

  38. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true

  39. gradle.properties org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true

  40. build.gradle

  41. プラグイン 適用 よく考えて • 必要なモジュールにだけプラグインを適用する • “subprojects” / “allprojects”を使う 、

    本当に実際に使用しているプラグインだけにする
  42. 参照するリポジトリを減らす よくあるやつ • jcenter() • mavenCentral() • maven { url

    “https://plugins.gradle.org/m2/” } • maven { url “https://maven.google.com” } • maven { url “https://jitpack.io” }
  43. 参照するリポジトリを減らす repositories { jcenter() mavenCentral() maven { url “https://plugins.gradle.org/m2/” }

    google() maven { url “https://jitpack.io” } }
  44. 参照するリポジトリを減らす repositories { maven { url “https://plugins.gradle.org/m2/” } google() }

    // これでいけるかも?
  45. 動的バージョン指定 避ける(+) • そもそも警告されていると思うが • 意図しないアップデートが発生する • バージョン比較 難しい •

    毎回バージョンチェックして遅くなる
  46. 動的バージョン指定 避ける(+) dependencies { // ダメ implementation ‘com.android.support:appcompat-v7:+’ }

  47. 最新 バージョンを探す goo.gl/qy5uJa プロジェクト 依存ライブラリ 、 最新バージョンを探してレポートするプラグイン

  48. 動的バージョン指定 避ける(+) こうすると ./gradlew dependencyUpdates -Drevision=release

  49. 動的バージョン指定 避ける(+) ------------------------------------------------------------ : Project Dependency Updates (report to plain

    text file) ------------------------------------------------------------ The following dependencies are using the latest release version: - com.android.support.test.espresso:espresso-core:3.0.1 - com.github.ben-manes:gradle-versions-plugin:0.17.0 - junit:junit:4.12 - com.android.support.test:runner:1.0.1 The following dependencies have later release versions: - com.android.support:appcompat-v7 [26.1.0 -> 27.0.1] - com.android.support.constraint:constraint-layout [1.0.2 -> 1.1.0-beta3] - com.android.tools.build:gradle [3.0.0 -> 3.1.0-alpha03] - org.jacoco:org.jacoco.agent [0.7.4.201502262128 -> 0.7.9] - org.jacoco:org.jacoco.ant [0.7.4.201502262128 -> 0.7.9]
  50. 動的バージョン指定 避ける(+) ------------------------------------------------------------ : Project Dependency Updates (report to plain

    text file) ------------------------------------------------------------ The following dependencies are using the latest release version: - com.android.support.test.espresso:espresso-core:3.0.1 - com.github.ben-manes:gradle-versions-plugin:0.17.0 - junit:junit:4.12 - com.android.support.test:runner:1.0.1 The following dependencies have later release versions: - com.android.support:appcompat-v7 [26.1.0 -> 27.0.1] - com.android.support.constraint:constraint-layout [1.0.2 -> 1.1.0-beta3] - com.android.tools.build:gradle [3.0.0 -> 3.1.0-alpha03] - org.jacoco:org.jacoco.agent [0.7.4.201502262128 -> 0.7.9] - org.jacoco:org.jacoco.ant [0.7.4.201502262128 -> 0.7.9]
  51. 動的バージョン指定 避ける(+) ------------------------------------------------------------ : Project Dependency Updates (report to plain

    text file) ------------------------------------------------------------ The following dependencies are using the latest release version: - com.android.support.test.espresso:espresso-core:3.0.1 - com.github.ben-manes:gradle-versions-plugin:0.17.0 - junit:junit:4.12 - com.android.support.test:runner:1.0.1 The following dependencies have later release versions: - com.android.support:appcompat-v7 [26.1.0 -> 27.0.1] - com.android.support.constraint:constraint-layout [1.0.2 -> 1.1.0-beta3] - com.android.tools.build:gradle [3.0.0 -> 3.1.0-alpha03] - org.jacoco:org.jacoco.agent [0.7.4.201502262128 -> 0.7.9] - org.jacoco:org.jacoco.ant [0.7.4.201502262128 -> 0.7.9]
  52. 不要な / 使用してないライブラリを避ける • でかいライブラリ 避ける • 同様 機能で、より軽量 ライブラリを使用する

    Jacksonより 、Gson / Moshiを選ぶ Guava 使わない • 詳しく   goo.gl/miu6aV
  53. インクリメンタルビルドを有効にする • 修正したクラスと、それに依存するクラスだけ コンパイルする

  54. インクリメンタルビルドを有効にする tasks.withType(JavaCompile) { options.fork = true }

  55. インクリメンタルビルドを有効にする tasks.withType(JavaCompile) { options.incremental = true options.fork = true }

  56. インクリメンタルビルドを有効にする • AutoValue / Glide / Butterknife / Dagger等、 APTを行うライブラリ

    、インクリメンタルビルドを無効 化する • Android Gradle Plugin(AGP) 3.0〜 APTもサポートされている…?
  57. None
  58. Android Gradle Plugin

  59. ソフトウェアを最新にする • AGP 3.0.0を使う • SDK tools / platform toolsも最新

    も を使う
  60. 不要なリソース コンパイルを避ける • “resConfig”で、必要ないローカリゼーションを 除外することができる • 自分 とこ 、日本語リソースしかないけど? →外部ライブラリ

    、そうでもない で ?
  61. 不要なリソース コンパイルを避ける android { defaultConfig { resConfigs “ja” } }

    // 日本語だけ
  62. 不要なリソース コンパイルを避ける android { defaultConfig { resConfigs “ja”,”en” } }

    // 日本語と英語
  63. 定数を使う android { compileSdkVersion 27 buildToolsVersion “27.0.0” defaultConfig { applicationId

    “com.example.android.hmori.sample” versionCode new Date().format(“ddMMyyHHmm”).toInteger() versionName “1.0” minSdkVersion 19 targetSdkVersion 27 } }
  64. 定数を使う android { compileSdkVersion 27 buildToolsVersion “27.0.0” defaultConfig { applicationId

    “com.example.android.hmori.sample” versionCode new Date().format(“ddMMyyHHmm”).toInteger() versionName “1.0” minSdkVersion 19 targetSdkVersion 27 } }
  65. 定数を使う def isRelease = project.hasProperty(“release”) android { compileSdkVersion 27 buildToolsVersion

    “27.0.0” defaultConfig { applicationId “com.example.android.hmori.sample” versionCode isRelease ? new Date().format(“ddMMyyHHmm”).toInteger() : 1 versionName “1.0” minSdkVersion 19 targetSdkVersion 27 } }
  66. ライブラリモジュールに分割する • Gradle 修正したモジュールだけコンパイルする • コンパイル結果 キャッシュされる • モジュール分割 +

    “configurationOnDemand” + 並列ビルドが効果的
  67. PNG crunchを無効にする • PNGをWebPに変換できないなら、 PNG crunchを使う • 無効にすることで、AGP ビルドごとにPNGを 再圧縮すること

    なくなる • AGP 3.0.0〜、PNG crunch 、 ”debug”ビルド時に 、デフォルトで無効
  68. PNG crunchを無効にする android { aaptOptions { cruncherEnabled = project.hasProperty(“ci”) }

    }
  69. lagacyなmultidexを避ける • minSdkVersion < 21 : legacyなmultidex • minSdkVersion >=

    21 : ネイティブでmultidexがサポートされている • 開発時 み、minSdkVersionを21以上になるように すれ 、開発時 ビルドが くなる
  70. lagacyなmultidexを避ける android { compileSdkVersion 27 buildToolsVersion “27.0.0” defaultConfig { applicationId

    “com.example.android.hmori.sample” versionCode 1 versionName “1.0” minSdkVersion 19 targetSdkVersion 27 } }
  71. lagacyなmultidexを避ける android { compileSdkVersion 27 buildToolsVersion “27.0.0” defaultConfig { applicationId

    “com.example.android.hmori.sample” versionCode 1 versionName “1.0” minSdkVersion rootProject.hasProperty(“lollipop”) ? 21 : 19 targetSdkVersion 27 } }
  72. CIで pre-dexライブラリを無効化する • pre-dex インクリメンタルビルドで 効果的 • CIでクリーンビルドするときに 無駄

  73. CIで pre-dexライブラリを無効化する android { dexOptions { preDexLibraries = !project.hasProperty(“ci”) }

    }
  74. ビルドキャッシュを有効にする • AGP 幾つか 生成物をキャッシュする • AGP 2.3.0〜 デフォルトで有効

  75. gradle.properties # Gradle specific org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true

  76. gradle.properties # Gradle specific org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true #

    Android specific android.enableBuildCache=true
  77. 新しいDEXコンパイラを有効にする • DEXコンパイラ 、.class → .dexに変換する • D8 より早く.dexファイルに変換する。 •

    しかも生成物 より小さくなる • しかも実行時パフォーマンスも改善する • AGP 3.1.0〜デフォルトで有効
  78. gradle.properties # Gradle specific org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true #

    Android specific android.enableBuildCache=true
  79. gradle.properties # Gradle specific org.gradle.daemon=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true #

    Android specific android.enableBuildCache=true android.enableD8=true
  80. Android Studio 設定

  81. 画像をWebPにする • WebPにすることで、画像サイズを削減できる • ビルド時にやる必要 ない • ビルド前にやっておくことで、 ビルド時間 改善される

  82. 画像をWebPにする • “drawable”フォルダを右クリック

  83. Instant Runを有効にする • 一定 コード追加やリソース修正で、 新しくAPKファイルを作成せずに実行できる • 特定 場合に 、アクティビティ

    再起動さえ いらない
  84. offline modeを有効にする • dependencyやプラグインを頻繁に更新しない な ら、offline modeで更新チェックを外すといい • ./gradlew --offline

  85. offline modeを有効にする • Preferences ”Build, Execution, Deployment”

  86. Profiling Your Build

  87. ビルドプロセスをプロファイルする • “gradlew --profile” • “gradlew --scan”(goo.gl/UT1Qtb) • そ 他:

    https://github.com/gradle/gradle-profiler
  88. ビルドプロセスをプロファイルする • “gradlew clean assembleDebug --profile”

  89. ビルドプロセスをプロファイルする • “gradlew clean assembleDebug --scan”

  90. ビルドプロセスをプロファイルする

  91. 結果

  92. 1回目 2回目 3回目 4回目 5回目 Before 2 min 37.376 sec

    1 min 43.3 sec 1 min 39.9 sec 1 min 37.8 sec 1 min 40.8 sec After 1 min 29 sec 1 min 11 sec 47 sec 46 sec 45 sec
  93. None