Add performance test of add/remove window
Also add support of multiple stats for manual benchmark. So it can provide more details of separated steps. Bug: 131727899 Test: atest WindowAddRemovePerfTest Change-Id: Ia0b49c4a5e139449ee313c6ccbbe500e13327b8a
This commit is contained in:
@@ -39,7 +39,6 @@ import android.widget.LinearLayout;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -51,9 +50,7 @@ import java.util.function.IntSupplier;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@LargeTest
|
||||
public class RelayoutPerfTest {
|
||||
private static final IWindowSession sSession = WindowManagerGlobal.getWindowSession();
|
||||
|
||||
public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
private int mIteration;
|
||||
|
||||
@Rule
|
||||
@@ -85,12 +82,6 @@ public class RelayoutPerfTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
getInstrumentation().getUiAutomation().executeShellCommand("input keyevent KEYCODE_WAKEUP");
|
||||
getInstrumentation().getUiAutomation().executeShellCommand("wm dismiss-keyguard");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelayout() throws Throwable {
|
||||
final Activity activity = mActivityRule.getActivity();
|
||||
@@ -154,8 +145,9 @@ public class RelayoutPerfTest {
|
||||
}
|
||||
|
||||
void runBenchmark(BenchmarkState state) throws RemoteException {
|
||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||
while (state.keepRunning()) {
|
||||
sSession.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
|
||||
session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
|
||||
mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
|
||||
mOutOverscanInsets, mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
|
||||
mOutOutsets, mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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
|
||||
*
|
||||
* http://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 android.wm;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.perftests.utils.ManualBenchmarkState;
|
||||
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
|
||||
import android.perftests.utils.PerfManualStatusReporter;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InsetsState;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import com.android.internal.view.BaseIWindow;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@LargeTest
|
||||
public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase {
|
||||
@Rule
|
||||
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
// Get the permission to use most window types.
|
||||
sUiAutomation.adoptShellPermissionIdentity();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
sUiAutomation.dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ManualBenchmarkTest(warmupDurationNs = WARMUP_DURATION, targetTestDurationNs = TEST_DURATION)
|
||||
public void testAddRemoveWindow() throws Throwable {
|
||||
new TestWindow().runBenchmark(mPerfStatusReporter.getBenchmarkState());
|
||||
}
|
||||
|
||||
private static class TestWindow extends BaseIWindow {
|
||||
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
|
||||
final Rect mOutFrame = new Rect();
|
||||
final Rect mOutContentInsets = new Rect();
|
||||
final Rect mOutStableInsets = new Rect();
|
||||
final Rect mOutOutsets = new Rect();
|
||||
final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
|
||||
new DisplayCutout.ParcelableWrapper();
|
||||
final InsetsState mOutInsetsState = new InsetsState();
|
||||
|
||||
TestWindow() {
|
||||
mLayoutParams.setTitle(TestWindow.class.getName());
|
||||
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
// Simulate as common phone window.
|
||||
mLayoutParams.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
|
||||
}
|
||||
|
||||
void runBenchmark(ManualBenchmarkState state) throws RemoteException {
|
||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||
long elapsedTimeNs = 0;
|
||||
while (state.keepRunning(elapsedTimeNs)) {
|
||||
// InputChannel cannot be reused.
|
||||
final InputChannel inputChannel = new InputChannel();
|
||||
|
||||
long startTime = SystemClock.elapsedRealtimeNanos();
|
||||
session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
|
||||
Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
|
||||
mOutOutsets, mOutDisplayCutout, inputChannel, mOutInsetsState);
|
||||
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
state.addExtraResult("add", elapsedTimeNsOfAdd);
|
||||
|
||||
startTime = SystemClock.elapsedRealtimeNanos();
|
||||
session.remove(this);
|
||||
final long elapsedTimeNsOfRemove = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
state.addExtraResult("remove", elapsedTimeNsOfRemove);
|
||||
|
||||
elapsedTimeNs = elapsedTimeNsOfAdd + elapsedTimeNsOfRemove;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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
|
||||
*
|
||||
* http://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 android.wm;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import android.app.UiAutomation;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public class WindowManagerPerfTestBase {
|
||||
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
|
||||
static final long NANOS_PER_S = 1000L * 1000 * 1000;
|
||||
static final long WARMUP_DURATION = 1 * NANOS_PER_S;
|
||||
static final long TEST_DURATION = 5 * NANOS_PER_S;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// In order to be closer to the real use case.
|
||||
sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
|
||||
sUiAutomation.executeShellCommand("wm dismiss-keyguard");
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,13 @@ package android.perftests.utils;
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -71,6 +76,8 @@ public final class ManualBenchmarkState {
|
||||
|
||||
private int mState = NOT_STARTED; // Current benchmark state.
|
||||
|
||||
private long mWarmupDurationNs = WARMUP_DURATION_NS;
|
||||
private long mTargetTestDurationNs = TARGET_TEST_DURATION_NS;
|
||||
private long mWarmupStartTime = 0;
|
||||
private int mWarmupIterations = 0;
|
||||
|
||||
@@ -79,12 +86,30 @@ public final class ManualBenchmarkState {
|
||||
// Individual duration in nano seconds.
|
||||
private ArrayList<Long> mResults = new ArrayList<>();
|
||||
|
||||
/** @see #addExtraResult(String, long) */
|
||||
private ArrayMap<String, ArrayList<Long>> mExtraResults;
|
||||
|
||||
// Statistics. These values will be filled when the benchmark has finished.
|
||||
// The computation needs double precision, but long int is fine for final reporting.
|
||||
private Stats mStats;
|
||||
|
||||
void configure(ManualBenchmarkTest testAnnotation) {
|
||||
if (testAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final long warmupDurationNs = testAnnotation.warmupDurationNs();
|
||||
if (warmupDurationNs >= 0) {
|
||||
mWarmupDurationNs = warmupDurationNs;
|
||||
}
|
||||
final long targetTestDurationNs = testAnnotation.targetTestDurationNs();
|
||||
if (targetTestDurationNs >= 0) {
|
||||
mTargetTestDurationNs = targetTestDurationNs;
|
||||
}
|
||||
}
|
||||
|
||||
private void beginBenchmark(long warmupDuration, int iterations) {
|
||||
mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
|
||||
mMaxIterations = (int) (mTargetTestDurationNs / (warmupDuration / iterations));
|
||||
mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
|
||||
Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
|
||||
mState = RUNNING;
|
||||
@@ -108,7 +133,7 @@ public final class ManualBenchmarkState {
|
||||
final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime;
|
||||
++mWarmupIterations;
|
||||
if (mWarmupIterations >= WARMUP_MIN_ITERATIONS
|
||||
&& timeSinceStartingWarmup >= WARMUP_DURATION_NS) {
|
||||
&& timeSinceStartingWarmup >= mWarmupDurationNs) {
|
||||
beginBenchmark(timeSinceStartingWarmup, mWarmupIterations);
|
||||
}
|
||||
return true;
|
||||
@@ -129,31 +154,69 @@ public final class ManualBenchmarkState {
|
||||
}
|
||||
}
|
||||
|
||||
private String summaryLine() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Summary: ");
|
||||
sb.append("median=").append(mStats.getMedian()).append("ns, ");
|
||||
sb.append("mean=").append(mStats.getMean()).append("ns, ");
|
||||
sb.append("min=").append(mStats.getMin()).append("ns, ");
|
||||
sb.append("max=").append(mStats.getMax()).append("ns, ");
|
||||
sb.append("sigma=").append(mStats.getStandardDeviation()).append(", ");
|
||||
sb.append("iteration=").append(mResults.size()).append(", ");
|
||||
sb.append("values=").append(mResults.toString());
|
||||
/**
|
||||
* Adds additional result while this benchmark is running. It is used when a sequence of
|
||||
* operations is executed consecutively, the duration of each operation can also be recorded.
|
||||
*/
|
||||
public void addExtraResult(String key, long duration) {
|
||||
if (mState != RUNNING) {
|
||||
return;
|
||||
}
|
||||
if (mExtraResults == null) {
|
||||
mExtraResults = new ArrayMap<>();
|
||||
}
|
||||
mExtraResults.computeIfAbsent(key, k -> new ArrayList<>()).add(duration);
|
||||
}
|
||||
|
||||
private static String summaryLine(String key, Stats stats, ArrayList<Long> results) {
|
||||
final StringBuilder sb = new StringBuilder(key);
|
||||
sb.append(" Summary: ");
|
||||
sb.append("median=").append(stats.getMedian()).append("ns, ");
|
||||
sb.append("mean=").append(stats.getMean()).append("ns, ");
|
||||
sb.append("min=").append(stats.getMin()).append("ns, ");
|
||||
sb.append("max=").append(stats.getMax()).append("ns, ");
|
||||
sb.append("sigma=").append(stats.getStandardDeviation()).append(", ");
|
||||
sb.append("iteration=").append(results.size()).append(", ");
|
||||
sb.append("values=");
|
||||
if (results.size() > 100) {
|
||||
sb.append(results.subList(0, 100)).append(" ...");
|
||||
} else {
|
||||
sb.append(results);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void fillStatus(Bundle status, String key, Stats stats) {
|
||||
status.putLong(key + "_median", stats.getMedian());
|
||||
status.putLong(key + "_mean", (long) stats.getMean());
|
||||
status.putLong(key + "_percentile90", stats.getPercentile90());
|
||||
status.putLong(key + "_percentile95", stats.getPercentile95());
|
||||
status.putLong(key + "_stddev", (long) stats.getStandardDeviation());
|
||||
}
|
||||
|
||||
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
Log.i(TAG, key + summaryLine());
|
||||
Log.i(TAG, summaryLine(key, mStats, mResults));
|
||||
final Bundle status = new Bundle();
|
||||
status.putLong(key + "_median", mStats.getMedian());
|
||||
status.putLong(key + "_mean", (long) mStats.getMean());
|
||||
status.putLong(key + "_percentile90", mStats.getPercentile90());
|
||||
status.putLong(key + "_percentile95", mStats.getPercentile95());
|
||||
status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
|
||||
fillStatus(status, key, mStats);
|
||||
if (mExtraResults != null) {
|
||||
for (int i = 0; i < mExtraResults.size(); i++) {
|
||||
final String subKey = key + "_" + mExtraResults.keyAt(i);
|
||||
final Stats stats = new Stats(mExtraResults.valueAt(i));
|
||||
Log.i(TAG, summaryLine(subKey, mStats, mResults));
|
||||
fillStatus(status, subKey, stats);
|
||||
}
|
||||
}
|
||||
instrumentation.sendStatus(Activity.RESULT_OK, status);
|
||||
}
|
||||
}
|
||||
|
||||
/** The annotation to customize the test, e.g. the duration of warm-up and target test. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ManualBenchmarkTest {
|
||||
long warmupDurationNs() default -1;
|
||||
long targetTestDurationNs() default -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
@@ -59,15 +59,15 @@ public class PerfManualStatusReporter implements TestRule {
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
mState.configure(description.getAnnotation(ManualBenchmarkState.ManualBenchmarkTest.class));
|
||||
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
base.evaluate();
|
||||
|
||||
mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
|
||||
description.getMethodName());
|
||||
mState.sendFullStatusReport(getInstrumentation(), description.getMethodName());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user