mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Follow-up to the hand-authored amethyst/src/main/baseline-prof.txt, which was a
stopgap: whole-package wildcards that compile methods which never run. This adds
the machinery to produce a real profile from a recorded journey.
./gradlew :amethyst:generateBaselineProfile
New :baselineprofile module (com.android.test + androidx.baselineprofile) with a
BaselineProfileRule journey, and :amethyst applies the plugin and consumes it.
Notes on the wiring, since three things needed working out:
- com.android.test must be applied WITHOUT a version. AGP is already on the
buildscript classpath, so `alias(libs.plugins.androidTest)` fails with
"already on the classpath with an unknown version".
- targetSdk belongs in defaultConfig, not testOptions, for a test module.
- :amethyst has a `channel` dimension, so the test module needs
missingDimensionStrategy("channel", "play") or the dependency is ambiguous.
The generator ran on a connected device: Macrobenchmark 1.5.0-alpha07 supports
non-rooted generation on API 33+, so no root or AOSP emulator was needed. It
produced 31,497 rules (3.1 MB).
WHAT THE GENERATED PROFILE DOES AND DOES NOT COVER — it captures cold-start on a
LOGGED-OUT app, not the ingest burst. The generator installs the release
applicationId (com.vitorpamplona.amethyst), which is a fresh install with no
account, so the journey recorded a login screen. Measured on the output: zero
rules for justConsume and only 33 of 31,497 rules marked hot. Capturing ingest
needs a journey against a logged-in app, which needs a seeded test identity —
a design decision (a key in the repo is not acceptable), so it is left open.
Both profiles are therefore kept, because they cover different things: the
hand-authored one covers ingest (measured: nterp 42.9% -> 4.2% of ingest worker
CPU, ~1.6x more ingest per core-second), the generated one covers startup and
class loading.
Verified they both reach the shipping artifact by inspecting assets/dexopt/
baseline.prof across variants:
11,425 bytes neither profile
16,341 bytes hand-authored only (benchmark build type)
25,541 bytes hand-authored + generated (RELEASE variant)
The generated profile only lands in `release`; the custom `benchmark` build type
gets just the hand-authored file, because the plugin wires generated profiles
into release variants. That is why the earlier ingest measurements — taken on
the benchmark build type — could not see it. The runtime effect of the generated
half is NOT measured here: the release APK is unsigned and could not be
installed on the test device.
The 3.1 MB generated file is committed on purpose (saveInSrc), so release builds
do not need a device attached at build time.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
51 lines
1.6 KiB
Kotlin
51 lines
1.6 KiB
Kotlin
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
|
|
plugins {
|
|
id("com.android.test")
|
|
alias(libs.plugins.androidxBaselineProfile)
|
|
}
|
|
|
|
android {
|
|
namespace = "com.vitorpamplona.amethyst.baselineprofile"
|
|
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
|
|
|
defaultConfig {
|
|
// Baseline Profile generation needs API 28+; non-rooted generation needs API 33+.
|
|
minSdk = 28
|
|
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
|
|
// :amethyst has a `channel` dimension (play/fdroid). The recorded journey is the
|
|
// same either way — ingest does not differ by store — so record against play and
|
|
// let the consumer's mergeIntoMain share one profile with both flavours.
|
|
missingDimensionStrategy("channel", "play")
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_21
|
|
targetCompatibility = JavaVersion.VERSION_21
|
|
}
|
|
|
|
// The app whose journeys are recorded.
|
|
targetProjectPath = ":amethyst"
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget.set(JvmTarget.JVM_21)
|
|
}
|
|
}
|
|
|
|
baselineProfile {
|
|
// Generate against a connected device. Macrobenchmark 1.2.0-alpha06+ can do this
|
|
// without root on API 33+; below that it needs root or an `aosp` Gradle Managed Device.
|
|
useConnectedDevices = true
|
|
}
|
|
|
|
dependencies {
|
|
implementation(libs.androidx.junit)
|
|
implementation(libs.androidx.runner)
|
|
implementation(libs.androidx.uiautomator)
|
|
implementation(libs.androidx.benchmark.macro.junit4)
|
|
}
|