Add baseline profile

Create proguard-rules.pro, disabling obfuscation.
The BaselineProfileGenerator needs to run on an AOSP emulator.
Classes in StartupBenchmark should run on a physical device to produce
reliable data.

The baseline-prof.txt file should be re-generated for each release.

Change-Id: Iaceb4e5c6d2f927dc545454a44dea43951dfaf6d
pull/2/head
Ben Weiss 3 years ago
parent 773b2640b3
commit 5b54858642
No known key found for this signature in database
GPG Key ID: 8424F9C1E763A74C

@ -4,3 +4,15 @@ This is the repo for the Now in Android app.
TODOs:
- Add note about version catalogs for dependencies
## Baseline profile
The baseline profile for this app is located at `app/src/main/baseline-prof.txt`.
It contains rules that enable AOT compilation of the critical user path taken during app launch.
For more information on baseline profiles, read [this document](https://developer.android.com/studio/profile/baselineprofiles).
| Note: The baseline profile needs to be re-generated for release builds that touched code which changes app startup.
To generate the baseline profile, select the `benchmark` build variant and run the
`BaselineProfileGenerator` benchmark test on an AOSP Android Emulator.
Then copy the resulting baseline profile from the emulator to `app/src/main/baseline-prof.txt`.

@ -0,0 +1,2 @@
#Proguard rules for the `benchmark` build type.
-dontobfuscate

@ -45,6 +45,12 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
benchmark {
initWith buildTypes.release
signingConfig signingConfigs.debug
matchingFallbacks = ['release']
proguardFiles 'benchmark-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -128,6 +134,7 @@ dependencies {
implementation libs.androidx.navigation.compose
implementation libs.androidx.window.manager
implementation libs.material3
implementation libs.androidx.profileinstaller
implementation libs.hilt.android
kapt libs.hilt.compiler

@ -0,0 +1,26 @@
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

@ -14,6 +14,7 @@
~ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.samples.apps.nowinandroid">
<uses-permission android:name="android.permission.INTERNET" />
@ -26,6 +27,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Nia">
<profileable android:shell="true" tools:targetApi="q" />
<activity
android:name=".MainActivity"
android:exported="true"

File diff suppressed because it is too large Load Diff

@ -36,5 +36,6 @@ class MainActivity : ComponentActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent { NiaApp(rememberSizeClass()) }
reportFullyDrawn()
}
}

@ -0,0 +1,55 @@
plugins {
id 'com.android.test'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.google.samples.apps.nowinandroid.benchmark'
compileSdk buildConfig.compileSdk
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
defaultConfig {
minSdk 23
targetSdk 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
// This benchmark buildType is used for benchmarking, and should function like your
// release build (for example, with minification on). It's signed with a debug key
// for easy local/CI testing.
benchmark {
debuggable true
signingConfig debug.signingConfig
matchingFallbacks = ['release']
}
}
targetProjectPath = ":app"
experimentalProperties["android.experimental.self-instrumenting"] = true
}
dependencies {
implementation libs.androidx.test.core
implementation libs.androidx.test.espresso.core
implementation libs.androidx.test.ext
implementation libs.androidx.test.runner
implementation libs.androidx.test.rules
implementation libs.androidx.test.uiautomator
implementation libs.androidx.benchmark.macro
implementation libs.androidx.profileinstaller
}
androidComponents {
beforeVariants(selector().all()) {
enabled = buildType == "benchmark"
}
}

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

@ -0,0 +1,51 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.By
import org.junit.Rule
import org.junit.Test
/**
* Generates a baseline profile which can be copied to `app/src/main/baseline-prof.txt`.
*/
@ExperimentalBaselineProfilesApi
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(
packageName = "com.google.samples.apps.nowinandroid"
) {
pressHome()
// This block defines the app's critical user journey. Here we are interested in
// optimizing for app startup. But you can also navigate and scroll
// through your most important UI.
startActivityAndWait()
device.waitForIdle()
listOf("Episodes", "Saved", "Following").forEach {
device.run {
findObject(By.text(it))
.click()
waitForIdle()
}
}
}
}

@ -0,0 +1,88 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.startup
import androidx.benchmark.macro.BaselineProfileMode.Disable
import androidx.benchmark.macro.BaselineProfileMode.Require
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupMode.COLD
import androidx.benchmark.macro.StartupMode.HOT
import androidx.benchmark.macro.StartupMode.WARM
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Run this benchmark from Studio to see startup measurements, and captured system traces
* for investigating your app's performance from a cold state.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class ColdStartupBenchmark : AbstractStartupBenchmark(COLD)
/**
* Run this benchmark from Studio to see startup measurements, and captured system traces
* for investigating your app's performance from a warm state.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class WarmStartupBenchmark : AbstractStartupBenchmark(WARM)
/**
* Run this benchmark from Studio to see startup measurements, and captured system traces
* for investigating your app's performance from a hot state.
*/
@RunWith(AndroidJUnit4ClassRunner::class)
class HotStartupBenchmark : AbstractStartupBenchmark(HOT)
/**
* Base class for benchmarks with different startup modes.
* Enables app startups from various states of baseline profile or [CompilationMode]s.
*/
abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun startupNoCompilation() = startup(CompilationMode.None())
@Test
fun startupBaselineProfileDisabled() = startup(
CompilationMode.Partial(baselineProfileMode = Disable, warmupIterations = 1)
)
@Test
fun startupBaselineProfile() = startup(CompilationMode.Partial(baselineProfileMode = Require))
@Test
fun startupFullCompilation() = startup(CompilationMode.Full())
private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
packageName = "com.google.samples.apps.nowinandroid",
metrics = listOf(StartupTimingMetric()),
compilationMode = compilationMode,
iterations = 10,
startupMode = startupMode,
setupBlock = {
pressHome()
}
) {
startActivityAndWait()
}
}

@ -1,7 +1,7 @@
[versions]
accompanist = "0.21.4-beta"
androidDesugarJdkLibs = "1.1.5"
androidGradlePlugin = "7.0.3"
androidGradlePlugin = "7.3.0-alpha03"
androidxActivity = "1.4.0"
androidxAppCompat = "1.3.0"
androidxCompose = "1.2.0-alpha03"
@ -12,10 +12,13 @@ androidxDataStore = "1.0.0"
androidxEspresso = "3.3.0"
androidxHiltNavigationCompose = "1.0.0-rc01"
androidxLifecycle = "2.4.0"
androidxMacroBenchmark = "1.1.0-beta04"
androidxNavigation = "2.4.0-rc01"
androidxProfileinstaller = "1.2.0-alpha02"
androidxWindowManager = "1.0.0"
androidxTest = "1.4.0"
androidxTestExt = "1.1.2"
androidxUiAutomator = "2.2.0"
coil = "2.0.0-rc01"
hilt = "2.41"
jacoco = "0.8.7"
@ -43,6 +46,7 @@ android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" }
androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" }
androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "androidxCompose" }
androidx-compose-material = { group = "androidx.compose.material", name = "material", version.ref = "androidxCompose" }
androidx-compose-material-window = {group = "androidx.compose.material", name = "material-window", version.ref="androidxMaterialWindow"}
@ -60,11 +64,14 @@ androidx-dataStore = { group = "androidx.datastore", name = "datastore", version
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" }
androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" }
androidx-window-manager = {module = "androidx.window:window", version.ref = "androidxWindowManager"}
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTest" }
androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" }
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTest" }
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTest" }
androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxTestExt" }
coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil"}
coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil"}
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }

@ -1,5 +1,6 @@
#Thu Feb 24 14:19:14 GMT 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

@ -29,6 +29,7 @@ dependencyResolutionManagement {
}
rootProject.name = "nowinandroid"
include ':app'
include ':benchmark'
include ':core-common'
include ':core-domain'
include ':core-database'

Loading…
Cancel
Save