Add SelectTopicBenchmark + improve for you actions

Change-Id: Ide9285c8305899a255cd1e4c067c679719d4f7a0

Use latest snapshot for macrobench for latest metrics

Change-Id: Idf21eb7842ff6de02e7ba961bb3490f29248c187
tm/experiment-select-topic
Tomáš Mlynarič 2 years ago
parent 21777aba65
commit 2ab010c211

@ -16,7 +16,6 @@
package com.google.samples.apps.nowinandroid.baselineprofile
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.bookmarks.goToBookmarksScreen
@ -31,13 +30,12 @@ import org.junit.Test
/**
* Generates a baseline profile which can be copied to `app/src/main/baseline-prof.txt`.
*/
@ExperimentalBaselineProfilesApi
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
@Test
fun generate() =
baselineProfileRule.collectBaselineProfile(PACKAGE_NAME) {
baselineProfileRule.collect(PACKAGE_NAME) {
// This block defines the app's critical user journey. Here we are interested in
// optimizing for app startup. But you can also navigate and scroll
// through your most important UI.

@ -18,10 +18,14 @@ package com.google.samples.apps.nowinandroid.foryou
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until
import androidx.test.uiautomator.untilHasChildren
import com.google.samples.apps.nowinandroid.flingElementDownUp
val topicSelectionRes: BySelector = By.res("forYou:topicSelection")
fun MacrobenchmarkScope.forYouWaitForContent() {
// Wait until content is loaded by checking if topics are loaded
device.wait(Until.gone(By.res("loadingWheel")), 5_000)
@ -32,44 +36,63 @@ fun MacrobenchmarkScope.forYouWaitForContent() {
obj.wait(untilHasChildren(), 60_000)
}
fun MacrobenchmarkScope.forYouGetSelectedTopics(): List<UiObject2> {
val topics = device.findObject(topicSelectionRes) ?: return emptyList()
return topics.findObjects(By.checked(true))
}
fun MacrobenchmarkScope.hasTopicSelected(): Boolean {
return forYouGetSelectedTopics().isNotEmpty()
}
fun MacrobenchmarkScope.forYouClearSelectedTopics() {
forYouGetSelectedTopics().forEach {
it.click()
device.waitForIdle()
}
}
/**
* Selects some topics, which will show the feed content for them.
* [recheckTopicsIfChecked] Topics may be already checked from the previous iteration.
*/
fun MacrobenchmarkScope.forYouSelectTopics(recheckTopicsIfChecked: Boolean = false) {
val topics = device.findObject(By.res("forYou:topicSelection"))
fun MacrobenchmarkScope.forYouSelectTopics(
recheckTopicsIfChecked: Boolean = false,
howManyToClick: Int = 3,
skip: Int = 0
) {
val topics = device.findObject(topicSelectionRes)
val topicsItems = topics.findObjects(By.checkable(true))
// Set gesture margin from sides not to trigger system gesture navigation
val horizontalMargin = 10 * topics.visibleBounds.width() / 100
topics.setGestureMargins(horizontalMargin, 0, horizontalMargin, 0)
// Select some topics to show some feed content
var index = 0
var index = skip
var visited = 0
while (visited < 3) {
while (visited < howManyToClick) {
// Selecting some topics, which will populate items in the feed.
val topic = topics.children[index % topics.childCount]
// Find the checkable element to figure out whether it's checked or not
val topicCheckIcon = topic.findObject(By.checkable(true))
val topicItem = topicsItems[index % topics.childCount]
// Topic icon may not be visible if it's out of the screen boundaries
// If that's the case, let's try another index
if (topicCheckIcon == null) {
if (topicItem == null) {
index++
continue
}
when {
// Topic wasn't checked, so just do that
!topicCheckIcon.isChecked -> {
topic.click()
!topicItem.isChecked -> {
topicItem.click()
device.waitForIdle()
}
// Topic was checked already and we want to recheck it, so just do it twice
recheckTopicsIfChecked -> {
repeat(2) {
topic.click()
topicItem.click()
device.waitForIdle()
}
}

@ -0,0 +1,72 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.foryou
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.MemoryUsageMetric
import androidx.benchmark.macro.MemoryUsageMetric.Mode.Max
import androidx.benchmark.macro.TraceSectionMetric
import androidx.benchmark.macro.TraceSectionMetric.Mode.Sum
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4ClassRunner::class)
@OptIn(ExperimentalMetricApi::class)
class SelectTopicBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun benchmarkCompilationNone() = benchmark(CompilationMode.None())
@Test
fun benchmarkCompilationBaselineProfile() = benchmark(CompilationMode.Partial())
@Test
fun benchmarkCompilationFull() = benchmark(CompilationMode.Full())
private fun benchmark(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
packageName = PACKAGE_NAME,
metrics = listOf(
FrameTimingMetric(),
MemoryUsageMetric(Max),
TraceSectionMetric("SingleTopicButton", Sum),
),
compilationMode = compilationMode,
iterations = 10,
setupBlock = {
// kill process to simulate COLD startup. [StartupMode.COLD] should be just used for startup benchmarks.
killProcess()
startActivityAndWait()
allowNotifications()
forYouWaitForContent()
// Clear any previously selected topics in setupBlock, so we have stable number of topics.
forYouClearSelectedTopics()
forYouSelectTopics(false, 1)
},
) {
// Select just one topic to see what recomposes and what's the impact
forYouSelectTopics(true, 1, 1)
}
}

@ -33,7 +33,7 @@ internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*, *, *, *>,
) {
commonExtension.apply {
compileSdk = 33
compileSdk = 34
defaultConfig {
minSdk = 21

@ -15,7 +15,7 @@ androidxDataStore = "1.0.0"
androidxEspresso = "3.5.0"
androidxHiltNavigationCompose = "1.0.0"
androidxLifecycle = "2.6.0-alpha05"
androidxMacroBenchmark = "1.1.1"
androidxMacroBenchmark = "1.2.0-SNAPSHOT"
androidxMetrics = "1.0.0-alpha03"
androidxNavigation = "2.5.3"
androidxProfileinstaller = "1.2.1"

@ -28,6 +28,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://androidx.dev/snapshots/latest/artifacts/repository") }
}
}
rootProject.name = "nowinandroid"

Loading…
Cancel
Save