Skip to content

Commit f57d365

Browse files
authored
Merge pull request #263 from android/tm/composition-tracing
Add composition tracing example
2 parents e091186 + ded1e1c commit f57d365

File tree

9 files changed

+137
-16
lines changed

9 files changed

+137
-16
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="Scroll List With Composition Tracing" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
3+
<module name="macrobenchmark.macrobenchmark.main" />
4+
<option name="TESTING_TYPE" value="3" />
5+
<option name="METHOD_NAME" value="scrollComposeList" />
6+
<option name="CLASS_NAME" value="com.example.macrobenchmark.benchmark.frames.FrameTimingBenchmark" />
7+
<option name="PACKAGE_NAME" value="" />
8+
<option name="TEST_NAME_REGEX" value="" />
9+
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
10+
<option name="EXTRA_OPTIONS" value="-e androidx.benchmark.perfettoSdkTracing.enable true" />
11+
<option name="RETENTION_ENABLED" value="No" />
12+
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
13+
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
14+
<option name="CLEAR_LOGCAT" value="false" />
15+
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
16+
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
17+
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
18+
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
19+
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
20+
<option name="DEBUGGER_TYPE" value="Auto" />
21+
<Auto>
22+
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
23+
<option name="SHOW_STATIC_VARS" value="true" />
24+
<option name="WORKING_DIR" value="" />
25+
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
26+
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
27+
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
28+
<option name="DEBUG_SANDBOX_SDK" value="false" />
29+
</Auto>
30+
<Hybrid>
31+
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
32+
<option name="SHOW_STATIC_VARS" value="true" />
33+
<option name="WORKING_DIR" value="" />
34+
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
35+
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
36+
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
37+
<option name="DEBUG_SANDBOX_SDK" value="false" />
38+
</Hybrid>
39+
<Java>
40+
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
41+
<option name="DEBUG_SANDBOX_SDK" value="false" />
42+
</Java>
43+
<Native>
44+
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
45+
<option name="SHOW_STATIC_VARS" value="true" />
46+
<option name="WORKING_DIR" value="" />
47+
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
48+
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
49+
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
50+
<option name="DEBUG_SANDBOX_SDK" value="false" />
51+
</Native>
52+
<Profilers>
53+
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
54+
<option name="STARTUP_PROFILING_ENABLED" value="false" />
55+
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
56+
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
57+
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
58+
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
59+
</Profilers>
60+
<method v="2">
61+
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
62+
</method>
63+
</configuration>
64+
</component>

MacrobenchmarkSample/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,32 @@ Alternatively, run the benchmarks from terminal with:
3838
./gradlew macrobenchmark:cC
3939
```
4040

41+
### Macrobenchmark with Composition Tracing
42+
Composition Tracing allows to run system tracing with information on when all Composables (re)compose.
43+
This gives you insights on where the UI spends majority of the time and helps you find jank.
44+
45+
To set up composition tracing for your app, follow our [documentation](https://developer.android.com/jetpack/compose/tooling/tracing).
46+
To get composition tracing when running a macrobenchmark, you also need to use `androidx.benchmark.perfettoSdkTracing.enable=true` instrumentation argument.
47+
48+
You can check the `Scroll List With Composition Tracing` run configuration that is part of the project,
49+
which runs the scroll compose list benchmark while also recording the information on composition.
50+
51+
It produces results like in the following table:
52+
```
53+
FrameTimingBenchmark_scrollComposeList
54+
%EntryRow (%Count min 5.0, median 6.0, max 6.0
55+
%EntryRow (%Ms min 10.2, median 11.8, max 16.2
56+
EntryRowCustomTraceCount min 5.0, median 6.0, max 6.0
57+
EntryRowCustomTraceMs min 10.0, median 11.7, max 16.1
58+
59+
frameDurationCpuMs P50 4.8, P90 6.8, P95 8.9, P99 15.3
60+
frameOverrunMs P50 -9.2, P90 -1.9, P95 266.9, P99 310.9
61+
Traces: Iteration 0 1 2 3 4 5 6 7 8 9
62+
```
63+
64+
And from there you can also delve into the system trace, which shows information on composition:
65+
![System trace with composition tracing](media/composition-tracing.png)
66+
4167
### Reporting Issues
4268

4369
You can report an [Issue with the sample](https://github.com/googlesamples/android-performance/issues) using this repository. If you find an issue with the Macrobenchmark library, report it using the [Issue Tracker](https://issuetracker.google.com/issues/new?component=975669&template=1519452).

MacrobenchmarkSample/app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ dependencies {
8989
implementation(libs.compose.material)
9090
implementation(libs.compose.ui)
9191
implementation(libs.compose.ui.tooling)
92+
implementation(libs.compose.runtime.tracing)
9293
implementation(libs.constraintlayout)
9394
implementation(libs.concurrentfutures)
9495
implementation(libs.core)

MacrobenchmarkSample/app/src/main/java/com/example/macrobenchmark/target/activity/clicklatency/ComposeActivity.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import androidx.compose.ui.semantics.semantics
4848
import androidx.compose.ui.semantics.testTagsAsResourceId
4949
import androidx.compose.ui.tooling.preview.Preview
5050
import androidx.compose.ui.unit.dp
51+
import androidx.tracing.trace
5152
import com.example.macrobenchmark.target.recyclerview.Entry
5253
import com.example.macrobenchmark.target.util.ClickTrace
5354

@@ -90,23 +91,25 @@ class ComposeActivity : ComponentActivity() {
9091
value = value,
9192
onValueChange = { value = it },
9293
placeholder = { Text("Enter text here") }
93-
)
94+
)
9495

9596
LazyColumn(
9697
modifier = Modifier
9798
.testTag("myLazyColumn")
9899
) {
99100
items(data, key = { it.contents }) { item ->
100-
EntryRow(entry = item,
101-
Modifier
101+
EntryRow(
102+
entry = item,
103+
modifier = Modifier
102104
.padding(8.dp)
103105
.clickable {
104106
ClickTrace.onClickPerformed()
105107
AlertDialog
106108
.Builder(this@ComposeActivity)
107109
.setMessage("Item clicked")
108110
.show()
109-
})
111+
}
112+
)
110113
}
111114
}
112115
}
@@ -124,7 +127,7 @@ class ComposeActivity : ComponentActivity() {
124127
}
125128

126129
@Composable
127-
private fun EntryRow(entry: Entry, modifier: Modifier = Modifier) {
130+
private fun EntryRow(entry: Entry, modifier: Modifier = Modifier) = trace("EntryRowCustomTrace") {
128131
Card(modifier = modifier) {
129132
Row(verticalAlignment = Alignment.CenterVertically) {
130133
Text(

MacrobenchmarkSample/gradle/libs.versions.toml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[versions]
2-
agp = "8.1.1"
2+
agp = "8.1.4"
33
activity = "1.7.2"
44
appcompat = "1.6.1"
5-
benchmark = "1.2.0-rc01"
6-
composeBom = "2023.09.02"
7-
composeCompiler = "1.4.7"
5+
benchmark = "1.2.1"
6+
composeBom = "2023.10.01"
7+
composeCompiler = "1.5.4"
88
constraintLayout = "2.1.4"
99
core = "1.12.0"
1010
coroutines = "1.6.4"
@@ -13,13 +13,15 @@ curtains = "1.2.4"
1313
dataStore = "1.0.0"
1414
espressoCore = "3.5.1"
1515
jUnit = "1.1.5"
16-
kotlin = "1.8.21"
16+
kotlin = "1.9.20"
1717
lifecycle = "2.6.2"
1818
material = "1.9.0"
1919
profileInstaller = "1.3.1"
2020
rules = "1.5.0"
21-
tracing = "1.1.0"
22-
uiAutomator = "2.3.0-alpha04"
21+
runtimeTracing = "1.0.0-alpha05"
22+
tracing = "1.3.0-alpha02"
23+
tracingPerfetto = "1.0.0"
24+
uiAutomator = "2.3.0-alpha05"
2325

2426
[libraries]
2527
androidx-rules = { module = "androidx.test:rules", version.ref = "rules" }
@@ -30,6 +32,7 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
3032
benchmark-junit = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmark" }
3133
compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" }
3234
compose-material = { group = "androidx.compose.material", name = "material" }
35+
compose-runtime-tracing = { module = "androidx.compose.runtime:runtime-tracing", version.ref = "runtimeTracing" }
3336
compose-ui = { group = "androidx.compose.ui", name = "ui" }
3437
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
3538
concurrentfutures = { group = "androidx.concurrent", name = "concurrent-futures-ktx", version.ref = "concurrentFutures" }
@@ -44,13 +47,15 @@ lifecycle = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", vers
4447
profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileInstaller" }
4548
squareup-curtains = { group = "com.squareup.curtains", name = "curtains", version.ref = "curtains" }
4649
tracing = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "tracing" }
50+
tracing-perfetto = { module = "androidx.tracing:tracing-perfetto", version.ref = "tracingPerfetto" }
51+
tracing-perfetto-binary = { module = "androidx.tracing:tracing-perfetto-binary", version.ref = "tracingPerfetto" }
4752
ui-automator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiAutomator" }
4853
viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
4954

5055
[plugins]
5156

5257
application = { id = "com.android.application", version.ref = "agp" }
53-
baselineprofile = { id = "androidx.baselineprofile", version.ref = "benchmark"}
58+
baselineprofile = { id = "androidx.baselineprofile", version.ref = "benchmark" }
5459
library = { id = "com.android.library", version.ref = "agp" }
5560
test = { id = "com.android.test", version.ref = "agp" }
5661
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Mon Mar 06 15:40:15 GMT 2023
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
55
zipStoreBase=GRADLE_USER_HOME
66
zipStorePath=wrapper/dists

MacrobenchmarkSample/macrobenchmark/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,10 @@ dependencies {
9191
implementation(libs.androidx.junit)
9292
implementation(libs.espresso)
9393
implementation(libs.ui.automator)
94+
95+
// Adds dependencies to enable running Composition Tracing from Macrobenchmarks.
96+
// These dependencies are already included with Macrobenchmark, but we have them here to specify the version.
97+
// For more information on Composition Tracing, check https://developer.android.com/jetpack/compose/tooling/tracing.
98+
implementation(libs.tracing.perfetto)
99+
implementation(libs.tracing.perfetto.binary)
94100
}

MacrobenchmarkSample/macrobenchmark/src/main/java/com/example/macrobenchmark/benchmark/frames/FrameTimingBenchmark.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
package com.example.macrobenchmark.benchmark.frames
1818

1919
import android.content.Intent
20+
import android.graphics.Point
2021
import androidx.benchmark.macro.CompilationMode
22+
import androidx.benchmark.macro.ExperimentalMetricApi
2123
import androidx.benchmark.macro.FrameTimingMetric
2224
import androidx.benchmark.macro.StartupMode
25+
import androidx.benchmark.macro.TraceSectionMetric
2326
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
2427
import androidx.test.ext.junit.runners.AndroidJUnit4
2528
import androidx.test.filters.LargeTest
@@ -67,12 +70,25 @@ class FrameTimingBenchmark {
6770
}
6871
// [END macrobenchmark_control_your_app]
6972

73+
@OptIn(ExperimentalMetricApi::class)
7074
@Test
7175
fun scrollComposeList() {
7276
benchmarkRule.measureRepeated(
7377
// [START_EXCLUDE]
7478
packageName = TARGET_PACKAGE,
75-
metrics = listOf(FrameTimingMetric()),
79+
metrics = listOf(
80+
FrameTimingMetric(),
81+
// Measure custom trace sections by name EntryRow (which is added to the EntryRow composable).
82+
// Mode.Sum measure combined duration and also how many times it occurred in the trace.
83+
// This way, you can estimate whether a composable recomposes more than it should.
84+
TraceSectionMetric("EntryRowCustomTrace", TraceSectionMetric.Mode.Sum),
85+
// This trace section takes into account the SQL wildcard character %,
86+
// which can find trace sections without knowing the full name.
87+
// This way, you can measure composables produced by the composition tracing
88+
// and measure how long they took and how many times they recomposed.
89+
// WARNING: This metric only shows results when running with composition tracing, otherwise it won't be visible in the outputs.
90+
TraceSectionMetric("%EntryRow (%", TraceSectionMetric.Mode.Sum),
91+
),
7692
// Try switching to different compilation modes to see the effect
7793
// it has on frame timing metrics.
7894
compilationMode = CompilationMode.None(),
@@ -100,7 +116,7 @@ class FrameTimingBenchmark {
100116
column.setGestureMargin(device.displayWidth / 5)
101117

102118
// Scroll down several times
103-
repeat(3) { column.fling(Direction.DOWN) }
119+
repeat(1) { column.drag(Point(column.visibleCenter.x, column.visibleBounds.top)) }
104120
}
105121
}
106122
}
Loading

0 commit comments

Comments
 (0)