言いたいことはそれだけか

KotlinとかAndroidとかが好きです。調べたことをメモします。٩( 'ω' )و

androidx.benchmarkの1.0.0がリリースされたので試してみる

Android Dev Summit 2019の下記のセッションで軽くふれられていた androidx.benchmark の1.0.0がリリースされたようなのでさわってみます。このセッションはbenchmarkの使い方を説明したものではないけど、色々と面白いのでおすすめです。

www.youtube.com

それではやっていきましょう。

0. Reference

Jetpack Benchmarkの公式referenceはこの辺り。

developer.android.com

API referenceはこの辺り。

developer.android.com

で、これらをどうやって使っていくかはこの辺りにまとまっています。

developer.android.com

1. Benchmark用のmoduleを作る

最初に紹介した動画でも言及されていたのですが、benchmarkを測る時はdebug modeをOFFにすることが奨励されています。 こういったconfigurationを他のアプリのモジュールから分離するために専用のmoduleを作ります。

Android StudioはBenchmark moduleを作るためのtemplateが用意されているのでこれを利用します。ただし、Android Studio 3.5系を使っている場合はこのtempleteを使うためには手動で下記の設定が必要です。

  1. Help > Edit Custom Properties をクリック。(「idea.propertiesのファイルが今ないから作る?」って聞かれたら Create を選択して下さい。)
  2. 下記を1行追加してAndroid Studioを再起動する
npw.benchmark.template.module=true

で、ここからはAndroid Studio 3.6からと共通。Templateを使ってbenchmark moduleを用意します。

  1. Projectを右クリックして New > Module を選択
  2. Moduleの選択肢が出てくるので Benchmark Module を選んで Next をクリック
  3. Module名とか色々変えたかったら変更して Finishをクリック

これでProject rootの下に benchmark moduleが作成されます。

このmoduleの build.gradle をみると androidx.benchmark:benchmark にすでに依存が付いています。ただしtemplateで指定されたversionが古いままだったりする😇1.0.0 だとpackage nameも変わっているので注意です。下記に変えてやりましょう。

androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.0.0'

また、gradleのsyncが下記のエラーで失敗したりする。

androidx.benchmark.AndroidBenchmarkRunner, in project benchmark, which is no longer valid as it has been moved to androidx.benchmark.junit4.AndroidBenchmarkRunner.

というわけで正しい依存をつけてエラーをとってやりましょう。

    defaultConfig {

-        testInstrumentationRunner 'androidx.benchmark.AndroidBenchmarkRunner'
+        testInstrumentationRunner 'androidx.benchmark.junit4.AndroidBenchmarkRunner'
    }

また、このmoduleの androidTest 下に作られたAndroidStudioにはdebug modeをOFFにする設定がすでに記述されています。便利!

project_root/benchmark/src/androidTest/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.muumuu.benchmark">

    <!--
      Important: disable debugging for accurate performance results

      In a com.android.library project, this flag must be disabled from this
      manifest, as it is not possible to override this flag from Gradle.
    -->
    <application
        android:debuggable="false"
        tools:ignore="HardcodedDebugMode"
        tools:replace="android:debuggable" />
</manifest>

2. benchmarkを書いていく

BenchmarkはInstrumentation testになります。benchmarkを作るには BenchmarkRule クラスを使っていきます。余談ですが、このクラスにはいくつかサブクラスが存在していて、Activity用のbenchmarkを測りたい時は ActivityTestRulectivityScenarioRuleを使うようです。 UIのbenchmarkを測る時は @UiThreadTestアノテーションを使います。

今回はActivityを作るのが面倒だったのでdata processingでbenchmarkを測っていきます。最近同僚に教えてもらったちょうどいい記事があるのでこれを試します。

blog.kotlin-academy.com

この記事の内容を軽く紹介すると、データサイズが大きい場合に、Kotlinの IterableSequence だとdata processのstepが2以上ある場合はSequence使ったほうが早いよーという話です。

こんな感じで 1から1000のlistを作ってSequenceIterableで全く同じ操作をしてそれぞれのbenchmarkを測ります。

@RunWith(AndroidJUnit4::class)
class SequenceBenchmark {

    @get:Rule
    val benchmarkRule = BenchmarkRule()

    private val dataSet = (1..1000).toList()

    @Test
    fun logSequence() {
        benchmarkRule.measureRepeated {
            dataSet.asSequence()
                .filter {
                    it.rem(2) == 0
                }
                .map {
                    it * 100
                }
                .average()
        }
    }

    @Test
    fun logIteration() {
        benchmarkRule.measureRepeated {
            dataSet
                .filter {
                    it.rem(2) == 0
                }
                .map {
                    it * 100
                }
                .average()
        }
    }
}

で、普通のtestを実行するようにtestを実行します。以下結果。確かにSequenceの方が早い。

Started running tests
benchmark:        63,038 ns SequenceBenchmark.logSequence
benchmark:        87,656 ns SequenceBenchmark.logIteration

感想

意外と簡単にシュッとできるので便利でした。Templeteがupdateされれば言うことない。
言いたいことは以上です。