Android Studio 3.5のbuild speedを測定してみた

この記事はトリスタinsideで書かれた記事です。
現在トリスタinsideはBOOK☆WALKER Tech Blogに統合されました。

アプリチームのKです。Android版ニコニコ漫画 / 読書メーターの開発を担当しています。 先月stable版のAndroid Studio3.5がリリースされました。 このバージョンは品質向上にフォーカスが当てられていることが発信されています1。 その一環であるbuild speedの改善が気になったので、どの程度向上されたのか測定してみました。

環境

測定を行なったハードのスペックは次の通りです。

  • OS: MacOS 10.14.6
  • プロセッサ: 2.7 GHz Intel Core i7
  • メモリ: 16 GB 2133 MHz LPDDR3

測定対象

Android Studio 3.5のbuild speedについては次のように述べられています。

Build Speed

For Android Studio 3.5 we made many speed improvements but a significant change is the addition of incremental build support to the top annotation processors including Glide, AndroidX data binding, Dagger, Realm, and Kotlin (KAPT). Incremental support can make a notable impact on build speed.

以上のことから次の2つを測定対象とします。

  1. Android Studio3.5自身のパフォーマンス
  2. incremental build support(incremental annotation)有効時のパフォーマンス

Android Studio3.5自身のパフォーマンス

ここではAndroid Studio 3.4のパフォーマンスと比較することで、Android Studio3.5がどの程度向上されたのかを確認します。 3.5自身の改善を観測するのが目的なのでgradle pluginやメモリ設定は3.4時のものを踏襲します。 具体的には次の通りです。

  • gradle: 5.2.1
  • gradle plugin: 3.3.1
  • メモリ: -Xmx4608M -XX:MaxMetaspaceSize=1024m

build対象としては漫画プロジェクト(debug build)を利用しました。 debug build時に作成されるapkは次の通りです。ある程度規模を想定することができると思います。

f:id:bookwalker_developers:20210930191423p:plain

incremental annotation関連を除くと具体的に改善された箇所がアナウンスされていない為、 今回はrebuildに要する時間を3.4.1 / 3.5のそれぞれで3回ずつ測定しました。結果は次の通りです。

47 actionable tasks: 46 executed, 1 up-to-date

3.4.1 3.5.0
1m 17s 1m 22s
1m 16s 1m 22s
1m 17s 1m 23s

今回の結果においては3.5の方が10%弱遅かったです。 試行回数が少ないですし確かなことは言えませんが、Android Studio自身を3.4から3.5へ更新するだけではbuild speed改善の恩恵は受けられなさそうに見えます。

incremental annotation有効時のパフォーマンス

先に3.5のrebuildのパフォーマンスを測定しましたが実際の開発時においてはrebuildを行う機会は少ないと思います。 そういう意味ではincremental annotation有効時のパフォーマンスを測定する方が、より現実に即した値を知る事ができると言えます。

incrementa annotationが有効になる条件

Android Studioの機能ではなくgradleの機能になります2。Kotlin(Kapt) + databindingを利用しているプロジェクトにおいて必須となる条件は次の通りです。

  • gradle: 5.4.1 or higher
  • android gradle plugin: 3.5.0 or higher
  • SDK build tools: 28.0.3 or higher
  • Kotlin(Kapt): 1.3.30 or higher

またprocessor(ライブラリ)側もincremental annotationに対応している必要があります。 ポピュラーなprocessorについては対応が進んでいますが、まだ未対応のものも多いです。

公式にある通り、利用しているannotation processorの中でincremental buildsをサポートしていないprocessorが1つでもあるとincremental annotationは無効になります。

Keep in mind, if you have to use one or more annotation processors that don't support incremental builds, annotation processing won't be incremental.

Javaのみで実現されている、またはdatabindingを利用していない等、プロジェクトによって必要な条件が変わってきます。 詳しくは公式をご確認ください。

測定対象のプロジェクトについて

以前@tukiyoが投稿した記事に書かれている通り、漫画プロジェクトではdatabindingを採用しているためincremental annotationを有効にすることで改善が期待できます。

漫画プロジェクトを使って測定しようとしましたがbuildエラーが発生しました。 一部抜粋します。

> Task :app:kaptDevelopDebugKotlin

[databinding] {"msg":"Could not find accessor java.lang.Object.isPresent","file":"app/src/main/res/layout/view_loading_more.xml","pos":[{"line0":45,"col0":34,"line1":45,"col1":67}]}

at android.databinding.annotationprocessor.ProcessDataBinding.doProcess(ProcessDataBinding.java:124)
at android.databinding.annotationprocessor.ProcessDataBinding.process(ProcessDataBinding.java:88)
at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.process(incrementalProcessors.kt)
at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:147)

レイアウトファイル上でエラーが発生しています。 bindしているオブジェクトが持つOptional型のプロパティにアクセスしている箇所ですが、 対象のプロパティがOptionalでなくObject型として認識されており、 アクセサ(isPresent)が見つからないためエラーとなっています。

調べてみたところAndroid gradle plugin3.5.0のbugのようでした3。 issuetrackerには回避方法も記載されていましたが、 漫画プロジェクトにおいては変更が大きくなるため3.5.0のpluginについては採用を見送っています。 構造上incremental annotationを有効にできれば改善できる可能性は高いので、 gradle pluginの問題が解消され次第再度調査する予定です。

とは言え、どの程度パフォーマンスが向上するのか興味はあるので、ここではgoogleが提供している sampleを利用して測定します。

incremental annotationを有効にする準備

いくつかのファイルを修正しました。次の通りです。

app/build.gradle

+    javaCompileOptions {
+        annotationProcessorOptions {
+            arguments = [
+                    "room.schemaLocation":"$projectDir/schemas".toString(),
+                    "room.incremental":"true",
+                    "room.expandProjection":"true"]
+        }
+    }

build.gradle

-    ext.kotlinVersion = '1.3.31'
+    ext.kotlinVersion = '1.3.50'

-    classpath 'com.android.tools.build:gradle:3.4.1'
+    classpath 'com.android.tools.build:gradle:3.5.0'

-    archLifecycleVersion = '2.1.0-rc01'
+    archLifecycleVersion = '2.2.0-alpha03'

-    daggerVersion = '2.23.2'
+    daggerVersion = '2.24'

-    roomVersion = '2.1.0'
+    roomVersion = '2.2.0-rc01'

gradle/wrapper/gradle-wrapper.properties

-    distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+    distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

incremental annotationを有効にする準備ができました。 測定に移ります。

測定

Kaptによるincremental annotationを有効にします。 gradle.propertiesに次の要素を追加します。

android.databinding.incremental=true
kapt.incremental.apt=true
kapt.use.worker.api=true

以降は追加前(incrementa annotaion無効)をA、追加後(incrementa annotation有効)をBとします。

incremental annotation有効時は次のようなbuild logが出力されます。

Incremental annotation processing (apt mode): true

[incremental apt] Changed files: [xxxx.java]
[incremental apt] Compiled sources directories: xxxxx
[incremental apt] Cache directory for incremental compilation: xxxx
[incremental apt] Changed classpath names: 

[INFO] Annotation processing complete, errors: 0, warnings: 2
[INFO] Annotation processor stats:
[INFO] androidx.room.RoomProcessor: total: 466 ms, init: 62 ms, 4 round(s): 404 ms, 0 ms, 0 ms, 0 ms
[INFO] androidx.lifecycle.LifecycleProcessor: total: 0 ms, init: 0 ms, 0 round(s): 
[INFO] dagger.internal.codegen.ComponentProcessor: total: 503 ms, init: 72 ms, 4 round(s): 192 ms, 235 ms, 3 ms, 1 ms
[INFO] dagger.android.processor.AndroidProcessor: total: 280 ms, init: 9 ms, 4 round(s): 270 ms, 1 ms, 0 ms, 0 ms
[INFO] android.databinding.annotationprocessor.ProcessDataBinding: total: 630 ms, init: 3 ms, 4 round(s): 447 ms, 67 ms, 63 ms, 50 ms
File Object History : []
Open Type Names     : []
Gen. Src Names      : []
Gen. Cls Names      : []
Agg. Gen. Src Names : []
Agg. Gen. Cls Names : []
[INFO] Annotation processing took 2907 ms

無効の際には次のようなlogが出力されます。

w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: android.databinding.annotationprocessor.ProcessDataBinding (DYNAMIC).

A・Bそれぞれ3回ずつ測定して得たAnnotation processing tookの値を次のテーブルにまとめました。

A B
3094 ms 2907 ms
2940 ms 3231 ms
4435 ms 2945 ms

試行回数が少ない為確かなことは言えませんが、今回の検証においては明確な違いは見受けられませんでした。 利用したプロジェクトが小規模であった事も関係していると思います。

終わりに

この記事ではAnroid Studio3.5のパフォーマンスにフォーカスして紹介してきました。 build speedを改善する方法は多数存在します。公式の方法は勿論、 中にはbuild中はAndroidStudioをサスペンドすることによって改善するといった方法も存在します。 build speedは生産性に直接関係してくる所なので、今後も改善に努めていきます。