Skip to content

Commit 77ca8ba

Browse files
authored
[MERGE] #341 -> develop
[FEAT/#341] Baseline Profile ์ ์šฉ
2 parents 4796890 + 905e5e8 commit 77ca8ba

14 files changed

Lines changed: 35141 additions & 4 deletions

File tree

โ€ŽREADME.mdโ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,16 @@
3333
<br>
3434

3535
## DEPENDENCY GRAPH
36-
<img src="https://github.com/user-attachments/assets/0d88e4fe-b170-4aae-83e6-c9017b2bc893"/>
36+
<img src="https://github.com/user-attachments/assets/a3f8cda6-0057-45ed-abac-714f25cb2243"/>
3737

3838
## MODULE & PACKAGE CONVENTION
3939

4040
```
4141
4242
๐Ÿ—ƒ๏ธapp
4343
44+
๐Ÿ—ƒ๏ธbaselineprofile
45+
4446
๐Ÿ—ƒ๏ธbuild-logic
4547
โ”ฃ ๐Ÿ“‚๏ธconvention
4648
โ”— ๐Ÿ“‚extension

โ€Žapp/build.gradle.ktsโ€Ž

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import java.util.Properties
21
import com.terning.build_logic.extension.setNamespace
2+
import java.util.Properties
33

44
plugins {
55
alias(libs.plugins.terning.application)
6+
alias(libs.plugins.baselineprofile)
67
alias(libs.plugins.kotlin.serialization)
78
}
89

@@ -56,6 +57,12 @@ android {
5657
)
5758
signingConfig = signingConfigs.getByName("release")
5859
}
60+
create("benchmark") {
61+
signingConfig = signingConfigs.getByName("debug")
62+
matchingFallbacks += listOf("release")
63+
isDebuggable = false
64+
proguardFiles("baseline-profiles-rules.pro")
65+
}
5966
}
6067
kotlinOptions {
6168
jvmTarget = libs.versions.jvmTarget.get()
@@ -84,6 +91,10 @@ dependencies {
8491
implementation(projects.data.tokenreissue)
8592
implementation(projects.data.search)
8693

94+
// baseline profile
95+
baselineProfile(projects.baselineprofile)
96+
implementation(libs.androidx.profileinstaller)
97+
8798
implementation(libs.timber)
8899
implementation(libs.kakao.user)
89100
implementation(libs.kotlinx.serialization.json)

โ€Žapp/src/release/generated/baselineProfiles/baseline-prof.txtโ€Ž

Lines changed: 17448 additions & 0 deletions
Large diffs are not rendered by default.

โ€Žapp/src/release/generated/baselineProfiles/startup-prof.txtโ€Ž

Lines changed: 17448 additions & 0 deletions
Large diffs are not rendered by default.

โ€Žbaselineprofile/.gitignoreโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import com.android.build.api.dsl.ManagedVirtualDevice
2+
3+
plugins {
4+
alias(libs.plugins.android.test)
5+
alias(libs.plugins.kotlin.android)
6+
alias(libs.plugins.baselineprofile)
7+
}
8+
9+
android {
10+
namespace = "com.terning.point.baselineprofile"
11+
compileSdk = libs.versions.compileSdk.get().toInt()
12+
13+
compileOptions {
14+
sourceCompatibility = JavaVersion.VERSION_1_8
15+
targetCompatibility = JavaVersion.VERSION_1_8
16+
}
17+
18+
kotlinOptions {
19+
jvmTarget = libs.versions.jvmTarget.get()
20+
}
21+
22+
defaultConfig {
23+
minSdk = libs.versions.minSdk.get().toInt()
24+
targetSdk = libs.versions.targetSdk.get().toInt()
25+
26+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
27+
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR"
28+
}
29+
30+
targetProjectPath = ":app"
31+
32+
// This code creates the gradle managed device used to generate baseline profiles.
33+
// To use GMD please invoke generation through the command line:
34+
// ./gradlew :app:generateBaselineProfile
35+
testOptions.managedDevices.devices {
36+
create<ManagedVirtualDevice>("pixel5Api33") {
37+
device = "Pixel 5"
38+
apiLevel = 33
39+
systemImageSource = "google"
40+
}
41+
}
42+
43+
}
44+
45+
// This is the configuration block for the Baseline Profile plugin.
46+
// You can specify to run the generators on a managed devices or connected devices.
47+
baselineProfile {
48+
managedDevices += "pixel5Api33"
49+
useConnectedDevices = false
50+
}
51+
52+
dependencies {
53+
implementation(libs.androidx.junit)
54+
implementation(libs.androidx.espresso.core)
55+
implementation(libs.androidx.uiautomator)
56+
implementation(libs.androidx.benchmark.macro.junit4)
57+
}
58+
59+
androidComponents {
60+
onVariants { v ->
61+
val artifactsLoader = v.artifacts.getBuiltArtifactsLoader()
62+
v.instrumentationRunnerArguments.put(
63+
"targetAppId",
64+
v.testedApks.map { artifactsLoader.load(it)?.applicationId }
65+
)
66+
}
67+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<manifest />
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.terning.point.baselineprofile
2+
3+
import androidx.benchmark.macro.junit4.BaselineProfileRule
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
import androidx.test.filters.LargeTest
6+
import androidx.test.platform.app.InstrumentationRegistry
7+
import org.junit.Rule
8+
import org.junit.Test
9+
import org.junit.runner.RunWith
10+
11+
/**
12+
* This test class generates a basic startup baseline profile for the target package.
13+
*
14+
* We recommend you start with this but add important user flows to the profile to improve their performance.
15+
* Refer to the [baseline profile documentation](https://d.android.com/topic/performance/baselineprofiles)
16+
* for more information.
17+
*
18+
* You can run the generator with the "Generate Baseline Profile" run configuration in Android Studio or
19+
* the equivalent `generateBaselineProfile` gradle task:
20+
* ```
21+
* ./gradlew :app:generateReleaseBaselineProfile
22+
* ```
23+
* The run configuration runs the Gradle task and applies filtering to run only the generators.
24+
*
25+
* Check [documentation](https://d.android.com/topic/performance/benchmarking/macrobenchmark-instrumentation-args)
26+
* for more information about available instrumentation arguments.
27+
*
28+
* After you run the generator, you can verify the improvements running the [StartupBenchmarks] benchmark.
29+
*
30+
* When using this class to generate a baseline profile, only API 33+ or rooted API 28+ are supported.
31+
*
32+
* The minimum required version of androidx.benchmark to generate a baseline profile is 1.2.0.
33+
**/
34+
@RunWith(AndroidJUnit4::class)
35+
@LargeTest
36+
class BaselineProfileGenerator {
37+
38+
@get:Rule
39+
val rule = BaselineProfileRule()
40+
41+
@Test
42+
fun generate() {
43+
// The application id for the running build variant is read from the instrumentation arguments.
44+
rule.collect(
45+
packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
46+
?: throw Exception("targetAppId not passed as instrumentation runner arg"),
47+
48+
// See: https://d.android.com/topic/performance/baselineprofiles/dex-layout-optimizations
49+
includeInStartupProfile = true
50+
) {
51+
// This block defines the app's critical user journey. Here we are interested in
52+
// optimizing for app startup. But you can also navigate and scroll through your most important UI.
53+
54+
// Start default activity for your app
55+
pressHome()
56+
startActivityAndWait()
57+
58+
// TODO Write more interactions to optimize advanced journeys of your app.
59+
// For example:
60+
// 1. Wait until the content is asynchronously loaded
61+
// 2. Scroll the feed content
62+
// 3. Navigate to detail screen
63+
64+
// Check UiAutomator documentation for more information how to interact with the app.
65+
// https://d.android.com/training/testing/other-components/ui-automator
66+
}
67+
}
68+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.terning.point.baselineprofile
2+
3+
import androidx.benchmark.macro.BaselineProfileMode
4+
import androidx.benchmark.macro.CompilationMode
5+
import androidx.benchmark.macro.StartupMode
6+
import androidx.benchmark.macro.StartupTimingMetric
7+
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
8+
import androidx.test.ext.junit.runners.AndroidJUnit4
9+
import androidx.test.filters.LargeTest
10+
import androidx.test.platform.app.InstrumentationRegistry
11+
import org.junit.Rule
12+
import org.junit.Test
13+
import org.junit.runner.RunWith
14+
15+
/**
16+
* This test class benchmarks the speed of app startup.
17+
* Run this benchmark to verify how effective a Baseline Profile is.
18+
* It does this by comparing [CompilationMode.None], which represents the app with no Baseline
19+
* Profiles optimizations, and [CompilationMode.Partial], which uses Baseline Profiles.
20+
*
21+
* Run this benchmark to see startup measurements and captured system traces for verifying
22+
* the effectiveness of your Baseline Profiles. You can run it directly from Android
23+
* Studio as an instrumentation test, or run all benchmarks for a variant, for example benchmarkRelease,
24+
* with this Gradle task:
25+
* ```
26+
* ./gradlew :baselineprofile:connectedBenchmarkReleaseAndroidTest
27+
* ```
28+
*
29+
* You should run the benchmarks on a physical device, not an Android emulator, because the
30+
* emulator doesn't represent real world performance and shares system resources with its host.
31+
*
32+
* For more information, see the [Macrobenchmark documentation](https://d.android.com/macrobenchmark#create-macrobenchmark)
33+
* and the [instrumentation arguments documentation](https://d.android.com/topic/performance/benchmarking/macrobenchmark-instrumentation-args).
34+
**/
35+
@RunWith(AndroidJUnit4::class)
36+
@LargeTest
37+
class StartupBenchmarks {
38+
39+
@get:Rule
40+
val rule = MacrobenchmarkRule()
41+
42+
@Test
43+
fun startupCompilationNone() =
44+
benchmark(CompilationMode.None())
45+
46+
@Test
47+
fun startupCompilationBaselineProfiles() =
48+
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
49+
50+
private fun benchmark(compilationMode: CompilationMode) {
51+
// The application id for the running build variant is read from the instrumentation arguments.
52+
rule.measureRepeated(
53+
packageName = InstrumentationRegistry.getArguments().getString("targetAppId")
54+
?: throw Exception("targetAppId not passed as instrumentation runner arg"),
55+
metrics = listOf(StartupTimingMetric()),
56+
compilationMode = compilationMode,
57+
startupMode = StartupMode.COLD,
58+
iterations = 10,
59+
setupBlock = {
60+
pressHome()
61+
},
62+
measureBlock = {
63+
startActivityAndWait()
64+
65+
// TODO Add interactions to wait for when your app is fully drawn.
66+
// The app is fully drawn when Activity.reportFullyDrawn is called.
67+
// For Jetpack Compose, you can use ReportDrawn, ReportDrawnWhen and ReportDrawnAfter
68+
// from the AndroidX Activity library.
69+
70+
// Check the UiAutomator documentation for more information on how to
71+
// interact with the app.
72+
// https://d.android.com/training/testing/other-components/ui-automator
73+
}
74+
)
75+
}
76+
}

โ€Žbuild.gradle.ktsโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ plugins {
1111
alias(libs.plugins.ksp) apply false
1212
alias(libs.plugins.kotlin.parcelize) apply false
1313
alias(libs.plugins.dokka) apply false
14+
alias(libs.plugins.baselineprofile) apply false
1415
}
1516

1617
buildscript {

0 commit comments

Comments
ย (0)