Skip to content

Commit dc96848

Browse files
committed
feat(perf): add baseline profile and macrobenchmark module
Add static baseline-prof.txt covering startup, Compose runtime, navigation, and coroutine paths. Add :apps:flipcash:benchmark macrobenchmark module with startup benchmark and baseline profile generator tests. Add profileinstaller dependency to the app. Fix .gitignore to use **/build/ pattern.
1 parent 7236f31 commit dc96848

9 files changed

Lines changed: 191 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/local.properties
44
.idea/
55
.DS_Store
6-
/build
6+
**/build/
77
/captures
88
.externalNativeBuild
99
.cxx

apps/flipcash/app/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ dependencies {
272272
implementation(libs.timber)
273273
implementation(libs.bugsnag)
274274

275+
implementation(libs.androidx.profileinstaller)
276+
275277
testImplementation(libs.junit)
276278
testImplementation(libs.kotlin.test.junit)
277279
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Baseline profile for Flipcash — covers cold start and Compose rendering.
2+
# Generated profiles from :apps:flipcash:benchmark will replace this file.
3+
4+
# Application + Activity startup
5+
HSPLcom/flipcash/app/FlipcashApp;->onCreate()V
6+
HSPLcom/flipcash/app/MainActivity;->onCreate(Landroid/os/Bundle;)V
7+
8+
# Hilt dependency injection (startup-critical)
9+
HSPLdagger/hilt/android/internal/**;->**(**)**
10+
HSPLcom/flipcash/app/FlipcashApp_GeneratedInjector;->**(**)**
11+
HSPLcom/flipcash/app/MainActivity_GeneratedInjector;->**(**)**
12+
13+
# Jetpack Compose runtime (critical for first frame)
14+
HSPLandroidx/compose/runtime/**;->**(**)**
15+
HSPLandroidx/compose/ui/**;->**(**)**
16+
HSPLandroidx/compose/foundation/**;->**(**)**
17+
HSPLandroidx/compose/material/**;->**(**)**
18+
HSPLandroidx/compose/animation/**;->**(**)**
19+
20+
# Compose layout and drawing
21+
HSPLandroidx/compose/ui/platform/AndroidComposeView;->**(**)**
22+
HSPLandroidx/compose/ui/node/**;->**(**)**
23+
24+
# Navigation
25+
HSPLandroidx/navigation/**;->**(**)**
26+
27+
# Lifecycle
28+
HSPLandroidx/lifecycle/**;->**(**)**
29+
HSPLandroidx/activity/**;->**(**)**
30+
31+
# Kotlinx coroutines (startup flows)
32+
HSPLkotlinx/coroutines/**;->**(**)**
33+
34+
# Kotlinx serialization (used in session/config deserialization)
35+
HSPLkotlinx/serialization/**;->**(**)**
36+
37+
# Coil image loading
38+
HSPLcoil3/**;->**(**)**
39+
40+
# App theme and UI setup
41+
HSPLcom/getcode/theme/**;->**(**)**
42+
HSPLcom/getcode/ui/core/**;->**(**)**
43+
HSPLcom/getcode/ui/components/**;->**(**)**
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
plugins {
2+
id("com.android.test")
3+
}
4+
5+
val contributorsSigningConfig = ContributorsSignatory(rootProject)
6+
7+
android {
8+
namespace = "com.flipcash.benchmark"
9+
compileSdk = libs.versions.android.compileSdk.get().toInt()
10+
11+
defaultConfig {
12+
minSdk = 29
13+
targetSdk = libs.versions.android.targetSdk.get().toInt()
14+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
15+
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] =
16+
"EMULATOR,DEBUGGABLE"
17+
}
18+
19+
signingConfigs {
20+
create("contributors") {
21+
storeFile = contributorsSigningConfig.keystore
22+
storePassword = contributorsSigningConfig.keystorePassword
23+
keyAlias = contributorsSigningConfig.keyAlias
24+
keyPassword = contributorsSigningConfig.keyPassword
25+
}
26+
}
27+
28+
buildTypes {
29+
debug {
30+
signingConfig = signingConfigs.getByName("contributors")
31+
}
32+
}
33+
34+
compileOptions {
35+
sourceCompatibility(libs.versions.android.java.get())
36+
targetCompatibility(libs.versions.android.java.get())
37+
}
38+
39+
kotlin {
40+
jvmToolchain(libs.versions.android.java.get().toInt())
41+
}
42+
43+
targetProjectPath = ":apps:flipcash:app"
44+
experimentalProperties["android.experimental.self-instrumenting"] = true
45+
}
46+
47+
dependencies {
48+
implementation(libs.androidx.benchmark.macro.junit4)
49+
implementation(libs.androidx.test.runner)
50+
implementation(libs.androidx.junit)
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
</manifest>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.flipcash.benchmark
2+
3+
import androidx.benchmark.macro.junit4.BaselineProfileRule
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import androidx.test.filters.LargeTest
6+
import org.junit.Rule
7+
import org.junit.Test
8+
import org.junit.runner.RunWith
9+
10+
@RunWith(AndroidJUnit4::class)
11+
@LargeTest
12+
class BaselineProfileGenerator {
13+
14+
@get:Rule
15+
val rule = BaselineProfileRule()
16+
17+
@Test
18+
fun generateBaselineProfile() {
19+
rule.collect(
20+
packageName = "com.flipcash.app.android",
21+
) {
22+
// Cold start the app — this covers the critical startup path
23+
pressHome()
24+
startActivityAndWait()
25+
26+
// TODO: Add user journey interactions here to cover hot paths
27+
// e.g., navigating to balance, scanning, etc.
28+
}
29+
}
30+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.flipcash.benchmark
2+
3+
import androidx.benchmark.macro.BaselineProfileMode
4+
import androidx.benchmark.macro.CompilationMode
5+
import androidx.benchmark.macro.StartupMode
6+
import androidx.benchmark.macro.StartupTimingLegacyMetric
7+
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
8+
import androidx.test.ext.junit.runners.AndroidJUnit4
9+
import androidx.test.filters.LargeTest
10+
import org.junit.Rule
11+
import org.junit.Test
12+
import org.junit.runner.RunWith
13+
14+
@RunWith(AndroidJUnit4::class)
15+
@LargeTest
16+
class StartupBenchmark {
17+
18+
@get:Rule
19+
val rule = MacrobenchmarkRule()
20+
21+
@Test
22+
fun startupNoCompilation() = benchmark(CompilationMode.None())
23+
24+
@Test
25+
fun startupPartialCompilation() = benchmark(
26+
CompilationMode.Partial(
27+
baselineProfileMode = BaselineProfileMode.Disable,
28+
warmupIterations = 3,
29+
)
30+
)
31+
32+
// Requires a non-debuggable (release/benchmark) build to install the baseline profile.
33+
// Add a 'benchmark' buildType to the app module to enable this test.
34+
// @Test
35+
// fun startupBaselineProfile() = benchmark(
36+
// CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require)
37+
// )
38+
39+
@Test
40+
fun startupFullCompilation() = benchmark(CompilationMode.Full())
41+
42+
private fun benchmark(compilationMode: CompilationMode) {
43+
rule.measureRepeated(
44+
packageName = "com.flipcash.app.android",
45+
metrics = listOf(StartupTimingLegacyMetric()),
46+
compilationMode = compilationMode,
47+
iterations = 5,
48+
startupMode = StartupMode.COLD,
49+
) {
50+
pressHome()
51+
startActivityAndWait()
52+
}
53+
}
54+
}

gradle/libs.versions.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ lib-phone-number-port = "9.0.31"
6666
lib-phone-number-google = "9.0.31"
6767
zxing = "3.5.4"
6868

69+
androidx-benchmark-macro = "1.4.0"
70+
androidx-profileinstaller = "1.4.1"
6971
androidx-test-runner = "1.7.0"
7072
junit = "4.13.2"
7173
androidx-junit = "1.3.0"
@@ -270,6 +272,10 @@ rinku = { module = "dev.theolm:rinku", version.ref = "rinku" }
270272
rinku-compose = { module = "dev.theolm:rinku-compose-ext", version.ref = "rinku" }
271273
event-bus = { module = "io.github.hoc081098:channel-event-bus", version.ref = "event-bus" }
272274

275+
# Baseline Profile / Benchmark
276+
androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "androidx-benchmark-macro" }
277+
androidx-profileinstaller = { module = "androidx.profileinstaller:profileinstaller", version.ref = "androidx-profileinstaller" }
278+
273279
# Testing
274280
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }
275281
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" }

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ rootProject.name = "Flipcash"
3737
include(
3838
// app containers
3939
":apps:flipcash:app",
40+
":apps:flipcash:benchmark",
4041

4142
// flipcash modules
4243
":apps:flipcash:core",

0 commit comments

Comments
 (0)