Add summing mode to TraceSectionMetric
Test: TraceSectionMetricTest
Relnote: """
Added TraceSectionMode("label", Mode.Sum), allowing measurement of
total time spent on multiple trace sections with the same label. For
instance, TraceSectionMetric("inflate", Mode.Sum) will report a metric
`inflateMs` for the total time in a trace spent on inflation.
Also removed API requirement, as TraceSectionMetric works together
with androidx.tracing.Trace back to lower API levels, with the use of
[forceEnableAppTracing](https://developer.android.com/reference/androidx/tracing/Trace#forceEnableAppTracing()).
"""
Additionally adds tracing of cache_miss and cache_hit for observing
cost of shader compilation for b/231455742
Change-Id: Id7b68e23f5ded4d20ab21771dbf9eb96d9dcfdb7
diff --git a/benchmark/benchmark-macro/api/public_plus_experimental_current.txt b/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
index e0b7810..69c6e99 100644
--- a/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
+++ b/benchmark/benchmark-macro/api/public_plus_experimental_current.txt
@@ -114,8 +114,15 @@
public final class TagKt {
}
- @RequiresApi(29) @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
- ctor public TraceSectionMetric(String sectionName);
+ @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
+ ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+ }
+
+ public enum TraceSectionMetric.Mode {
+ method public static androidx.benchmark.macro.TraceSectionMetric.Mode valueOf(String name) throws java.lang.IllegalArgumentException;
+ method public static androidx.benchmark.macro.TraceSectionMetric.Mode[] values();
+ enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode First;
+ enum_constant public static final androidx.benchmark.macro.TraceSectionMetric.Mode Sum;
}
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
index 8fc7256..98120bd 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
@@ -16,7 +16,6 @@
package androidx.benchmark.macro
-import android.annotation.SuppressLint
import androidx.benchmark.macro.perfetto.PerfettoTraceProcessor
import androidx.benchmark.perfetto.PerfettoHelper
import androidx.test.filters.MediumTest
@@ -38,43 +37,52 @@
).absolutePath
@Test
- fun activityThreadMain() = verifySingleMetric(
+ fun activityThreadMain() = verifyFirstSum(
tracePath = api24ColdStart,
packageName = Packages.TEST,
sectionName = "ActivityThreadMain",
- expectedMs = 12.639
+ expectedFirstMs = 12.639
)
@Test
- fun activityStart() = verifySingleMetric(
+ fun activityStart() = verifyFirstSum(
tracePath = api24ColdStart,
packageName = Packages.TEST,
sectionName = "activityStart",
- expectedMs = 81.979
+ expectedFirstMs = 81.979
)
@Test
- fun startActivityAndWait() = verifySingleMetric(
+ fun startActivityAndWait() = verifyFirstSum(
tracePath = api24ColdStart,
packageName = Packages.TEST,
sectionName = "startActivityAndWait",
- expectedMs = 1_110.689
+ expectedFirstMs = 1_110.689
)
@Test
- fun launching() = verifySingleMetric(
+ fun launching() = verifyFirstSum(
tracePath = api24ColdStart,
packageName = Packages.TEST,
sectionName = "launching: androidx.benchmark.integration.macrobenchmark.target",
- expectedMs = 269.947
+ expectedFirstMs = 269.947
)
@Test
- fun section1_2() = verifySingleMetric(
+ fun section1_2() = verifyFirstSum(
tracePath = commasInSliceNames,
packageName = Packages.TARGET,
sectionName = "section1,2",
- expectedMs = 0.006615
+ expectedFirstMs = 0.006615
+ )
+
+ @Test
+ fun multiSection() = verifyFirstSum(
+ tracePath = api24ColdStart,
+ packageName = Packages.TARGET,
+ sectionName = "inflate",
+ expectedFirstMs = 13.318, // first inflation
+ expectedSumMs = 43.128 // total inflation
)
companion object {
@@ -85,16 +93,16 @@
apiLevel = 24
)
- @SuppressLint("NewApi") // we use a fixed trace - ignore for TraceSectionMetric
- private fun verifySingleMetric(
+ private fun verifyMetric(
tracePath: String,
packageName: String,
sectionName: String,
+ mode: TraceSectionMetric.Mode,
expectedMs: Double
) {
assumeTrue(PerfettoHelper.isAbiSupported())
- val metric = TraceSectionMetric(sectionName)
+ val metric = TraceSectionMetric(sectionName, mode)
val expectedKey = sectionName + "Ms"
metric.configure(packageName = packageName)
@@ -108,5 +116,28 @@
assertEquals(setOf(expectedKey), iterationResult.singleMetrics.keys)
assertEquals(expectedMs, iterationResult.singleMetrics[expectedKey]!!, 0.001)
}
+
+ private fun verifyFirstSum(
+ tracePath: String,
+ packageName: String,
+ sectionName: String,
+ expectedFirstMs: Double,
+ expectedSumMs: Double = expectedFirstMs // default implies only one matching section
+ ) {
+ verifyMetric(
+ tracePath = tracePath,
+ packageName = packageName,
+ sectionName = sectionName,
+ mode = TraceSectionMetric.Mode.First,
+ expectedMs = expectedFirstMs
+ )
+ verifyMetric(
+ tracePath = tracePath,
+ packageName = packageName,
+ sectionName = sectionName,
+ mode = TraceSectionMetric.Mode.Sum,
+ expectedMs = expectedSumMs
+ )
+ }
}
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index db769c5..e8fac44 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -269,19 +269,38 @@
}
/**
- * Captures the time taken by a trace section - a named begin / end pair matching the provided name.
+ * Captures the time taken by named trace section - a named begin / end pair matching the provided
+ * [sectionName].
*
- * Always selects the first instance of a trace section captured during a measurement.
+ * Select how matching sections are resolved into a duration metric with [mode].
*
* @see androidx.tracing.Trace.beginSection
* @see androidx.tracing.Trace.endSection
* @see androidx.tracing.trace
*/
-@RequiresApi(29) // Remove once b/182386956 fixed, as app tag may be needed for this to work.
@ExperimentalMetricApi
public class TraceSectionMetric(
- private val sectionName: String
+ private val sectionName: String,
+ private val mode: Mode = Mode.First
) : Metric() {
+ enum class Mode {
+ /**
+ * Captures the duration of the first instance of `sectionName` in the trace.
+ *
+ * When this mode is used, no measurement will be reported if the named section does
+ * not appear in the trace.
+ */
+ First,
+
+ /**
+ * Captures the sum of all instances of `sectionName` in the trace.
+ *
+ * When this mode is used, a measurement of `0` will be reported if the named section
+ * does not appear in the trace
+ */
+ Sum
+ }
+
internal override fun configure(packageName: String) {
}
@@ -296,16 +315,36 @@
captureInfo: CaptureInfo,
perfettoTraceProcessor: PerfettoTraceProcessor
): IterationResult {
- val slice = perfettoTraceProcessor.querySlices(sectionName).firstOrNull()
- return if (slice == null) {
- IterationResult.EMPTY
- } else IterationResult(
- singleMetrics = mapOf(
- sectionName + "Ms" to slice.dur / 1_000_000.0
- ),
- sampledMetrics = emptyMap(),
- timelineRangeNs = slice.ts..slice.endTs
- )
+ val slices = perfettoTraceProcessor.querySlices(sectionName)
+
+ return when (mode) {
+ Mode.First -> {
+ val slice = slices.firstOrNull()
+ if (slice == null) {
+ IterationResult.EMPTY
+ } else IterationResult(
+ singleMetrics = mapOf(
+ sectionName + "Ms" to slice.dur / 1_000_000.0
+ ),
+ sampledMetrics = emptyMap(),
+ timelineRangeNs = slice.ts..slice.endTs
+ )
+ }
+ Mode.Sum -> {
+ // note, this duration assumes non-reentrant slices
+ val durMs = slices.sumOf { it.dur } / 1_000_000.0
+ IterationResult(
+ singleMetrics = mapOf(sectionName + "Ms" to durMs),
+ sampledMetrics = emptyMap(),
+ timelineRangeNs = if (slices.isEmpty()) {
+ null
+ } else {
+ // parens added to make ktlint happy
+ (slices.minOf { it.ts })..(slices.maxOf { it.endTs })
+ }
+ )
+ }
+ }
}
}
diff --git a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
index cb9e113..e76e719 100644
--- a/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/compose/integration/macrobenchmark/SmallListStartupBenchmark.kt
@@ -17,16 +17,20 @@
package androidx.compose.integration.macrobenchmark
import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.TraceSectionMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.testutils.createStartupCompilationParams
+import androidx.testutils.getStartupMetrics
import androidx.testutils.measureStartup
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
+@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
class SmallListStartupBenchmark(
@@ -36,10 +40,25 @@
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
+ /**
+ * Temporary, tracking for b/231455742
+ *
+ * Note that this tracing only exists on more recent API levels
+ */
+ private val metrics = getStartupMetrics() + if (startupMode == StartupMode.COLD) {
+ listOf(
+ TraceSectionMetric("cache_hit", TraceSectionMetric.Mode.Sum),
+ TraceSectionMetric("cache_miss", TraceSectionMetric.Mode.Sum)
+ )
+ } else {
+ emptyList()
+ }
+
@Test
fun startup() = benchmarkRule.measureStartup(
compilationMode = compilationMode,
startupMode = startupMode,
+ metrics = metrics,
packageName = "androidx.compose.integration.macrobenchmark.target"
) {
action = "androidx.compose.integration.macrobenchmark.target.LAZY_COLUMN_ACTIVITY"
diff --git a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
index 7d0807c..4366f743 100644
--- a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
+++ b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
@@ -20,6 +20,7 @@
import android.os.Build
import androidx.benchmark.macro.BaselineProfileMode
import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.Metric
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingLegacyMetric
import androidx.benchmark.macro.StartupTimingMetric
@@ -84,10 +85,11 @@
startupMode: StartupMode,
packageName: String,
iterations: Int = 10,
+ metrics: List<Metric> = getStartupMetrics(),
setupIntent: Intent.() -> Unit = {}
) = measureRepeated(
packageName = packageName,
- metrics = getStartupMetrics(),
+ metrics = metrics,
compilationMode = compilationMode,
iterations = iterations,
startupMode = startupMode,