The Simplest Guide To Generate Baseline Profiles Continuously
How to empower builds with GitHub Actions
This article intends to talk about Macrobenchmark, Baseline Profiles, and GitHub Actions. Basic knowledge of these subjects is required ahead. Bear in mind that lessons learned here related to GitHub Actions can be ported to any other type of project.
Over the past years, Google has been working on many updates regarding Performance improvement. Baseline Profiles is one of the latest tools released by the company and they claim it can improve Android apps startup time by up to 40%. If you ever tried Baseline Profiles, you’re probably aware that generating rules, even with the Macrobenchmark library, might be very painful. This article provides an easy way to automatically generate these rules, no matter the size of your project as long it’s versioned on GitHub.
Whether choosing to start a new project or not, yours should set
compileSdk 32
or higher and support Android Gradle Plugin 7.3.0 or higher.
Note: Gradle Kotlin DSL of the following scripts is available here.
/**
*
* :app Gradle file
*
**/
android {
compileSdk 33
}
/**
*
* Top-level Gradle file
*
**/
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'com.android.test' version '7.3.0' apply false
}
#1 — (Optional) Create an empty Android project
#2 — Add Macrobenchmark library to your project
#3 — Create a benchmark-rules.pro
file, under the app
directory, to make sure Proguard won't obfuscate the benchmark build variant and set it as the Proguard file on thebenchmark
build type
# Benchmark-rules.pro
# Disables obfuscation for benchmark builds.
-dontobfuscate
/**
*
* :app Gradle file
*
**/
android {
buildTypes {
benchmark {
signingConfig signingConfigs.debug
proguardFiles("benchmark-rules.pro")
matchingFallbacks = ['release']
debuggable false
}
}
}
#4 — Add at least one Managed Device
import com.android.build.api.dsl.ManagedVirtualDevice
/**
*
* :benchmark Gradle file
*
**/
android {
testOptions {
managedDevices {
devices {
pixel2Api31(ManagedVirtualDevice) {
device = "Pixel 2"
apiLevel = 31
systemImageSource = "aosp"
}
}
}
}
}
#5 — Add at least one @Test
to collect Baseline Profiles
/**
*
* :benchmark module
*
**/
package com.example.benchmark
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
*
* ExperimentalBaselineProfilesApi is required so that
* macrobenchmark tests can be skipped when running
* the :benchmark:pixel2Api31BenchmarkAndroidTest task with
* androidx.benchmark.enabledRules=BaselineProfile
* as an argument. Check out actions.yml file.
*
**/
@OptIn(ExperimentalBaselineProfilesApi::class)
class BaselineProfileGenerator {
@get:Rule
val baselineProfileRule = BaselineProfileRule()
/**
*
* @packageName Same as your applicationId (:app Gradle file)
*
**/
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(
packageName = "com.example.myapplication",
profileBlock = {
startActivityAndWait()
}
)
}
#6 — Add the GitHub Actions script
# Workflow name
name: baseline-profiles
# Workflow title
run-name: ${{ github.actor }} requested a workflow
# Event trigger so this actions gets executed every time a push is made on the main branch
# Change this event to what suits your project best.
# Read more at https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
on:
push:
branches:
- main
# Environment variables (Optional)
# Small projects might have signingConfigs locally. This could lead to failures on GitHub Actions.
# If that's the case, upload your properties defined locally to GitHub Secrets.
# On your signingConfigs, you can recover GitHub Secrets using: variable = System.getenv("VARIABLE")
# Then uncomment this block properly defining your uploaded variables
# env:
# VARIABLE: ${{ secrets.VARIABLE }}
# Read more at https://docs.github.com/en/actions/security-guides/encrypted-secrets
# Jobs to executed on GitHub machines
jobs:
# Job name
generate-baseline-profiles:
# Operating system where the job gets to be executed
runs-on: macos-latest
# Job steps
steps:
# Checks your code out on the machine
- uses: actions/checkout@v3
# Sets java up
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
# Sets gradle up
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
# Grants execute permission to gradle (safety step)
- name: Grant Permissions to gradlew
run: chmod +x gradlew
# Cleans managed device if previously settle and space currently is not available
- name: Clean Managed Devices
run: ./gradlew cleanManagedDevices --unused-only
# Generates Baseline Profile
- name: Generate Baseline Profile
## swiftshader_indirect:
## Required to use software acceleration on machines that can't use hardware acceleration
## enabledRules=BaselineProfile:
## Required to skip macrobenchmark tests.
## Read more at https://developer.android.com/topic/performance/benchmarking/benchmarking-in-ci#real-devices
## Read more at https://developer.android.com/topic/performance/benchmarking/macrobenchmark-overview#configuration-errors
## gradle.workers.max:
## Required to avoid running multiple managed device tests concurrently. Read more at https://issuetracker.google.com/issues/193118030
run: ./gradlew :benchmark:pixel2Api31BenchmarkAndroidTest -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile -Dorg.gradle.workers.max=4
# Sets the Baseline Profile on its proper place so it gets correctly bundled into Play Store
- name: Move & Rename Baseline Profiles
run: |
mv -f benchmark/build/outputs/managed_device_android_test_additional_output/pixel2Api31/BaselineProfileGenerator_startup-baseline-prof.txt app/src/main/baseline-prof.txt
# Commits the generated Baseline Profile to your origin/remote
- name: Commit Baseline Profiles
run: |
git config --global user.name 'Baseline Profiles - GitHub Actions'
git config --global user.email 'github@actions'
git add app/src/main/baseline-prof.txt
git commit -m "Generate baseline profiles"
git push
#7 — Merge all changes into the main
branch
EOF — In about ten minutes, a successful job should be present on the Actions tab and, finally, the last commit in your repository will present changes of a recently added baseline-prof.txt
file.
GitHub Actions is a very powerful tool to generate Baseline Profile rules in a scalable way. By properly invoking other actions/*
and replacing run
commands on the *.yml
file, many other workflows could be created. GitHub Actions is one of the best ways to automate workflows that suits your project best.
Now, tell me, how do you think Baseline Profiles or GitHub Actions could come in handy? Tell me more about your perspectives in the comments. Thank you for clapping & sharing if you enjoyed it, and don’t forget to follow me to stay tuned for upcoming articles! =}
References
- Macrobenchmark
- Baseline Profiles
- GitHub Actions
- Performance improving overview
- Introduction to Performance — MAD skills
- Improving App Performance with Baseline Profiles
- Manually create a Baseline Profile
- Setup the Android 12 SDK
- Android Gradle plugin 7.3.0
- Writing a Macrobenchmark
- Android Code Search — Profile examples
- Macrobenchmark — Configuration errors
- Baseline Profiles — Known issues