OSDN Git Service

Introduce initial perf test for framework/base/core.
authorSeigo Nonaka <nona@google.com>
Tue, 31 May 2016 08:08:40 +0000 (17:08 +0900)
committerSeigo Nonaka <nona@google.com>
Thu, 2 Jun 2016 09:51:45 +0000 (18:51 +0900)
Bug: 28980976
Change-Id: Ib2ccbad8d6657dcae08c2aafa9a63bcc52b88a04

core/tests/perftests/Android.mk [new file with mode: 0644]
core/tests/perftests/AndroidManifest.xml [new file with mode: 0644]
core/tests/perftests/src/android/perftest/BenchmarkState.java [new file with mode: 0644]
core/tests/perftests/src/android/widget/StubActivity.java [new file with mode: 0644]
core/tests/perftests/src/android/widget/TextViewSetTextLocalePerfTest.java [new file with mode: 0644]

diff --git a/core/tests/perftests/Android.mk b/core/tests/perftests/Android.mk
new file mode 100644 (file)
index 0000000..fb2faba
--- /dev/null
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := CorePerfTests
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/perftests/AndroidManifest.xml b/core/tests/perftests/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..b67e57f
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.core.frameworks.perftests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.widget.StubActivity" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.core.frameworks.perftests"/>
+
+</manifest>
diff --git a/core/tests/perftests/src/android/perftest/BenchmarkState.java b/core/tests/perftests/src/android/perftest/BenchmarkState.java
new file mode 100644 (file)
index 0000000..146582c
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 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.perftest;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Provides a benchmark framework.
+ *
+ * Example usage:
+ * // Executes the code while keepRunning returning true.
+ *
+ * public void sampleMethod() {
+ *     BenchmarkState state = new BenchmarkState();
+ *
+ *     int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ *     while (state.keepRunning()) {
+ *        int[] dest = new int[src.length];
+ *        System.arraycopy(src, 0, dest, 0, src.length);
+ *     }
+ *
+ *     System.out.println(state.summaryLine());
+ * }
+ */
+public class BenchmarkState {
+  private static final int NOT_STARTED = 1;  // The benchmark has not started yet.
+  private static final int RUNNING = 2;  // The benchmark is running.
+  private static final int FINISHED = 3;  // The benchmark has stopped.
+
+  private int mState = NOT_STARTED;  // Current benchmark state.
+
+  private long mNanoPreviousTime = 0;  // Previously captured System.nanoTime().
+  private long mNanoFinishTime = 0;  // Finish if System.nanoTime() returns after than this value.
+  private long mNanoTimeLimit = 1 * 1000 * 1000 * 1000;  // 1 sec. Default time limit.
+
+  // Statistics. These values will be filled when the benchmark has finished.
+  private long mMedian = 0;
+  private double mMean = 0.0;
+  private double mStandardDeviation = 0.0;
+
+  // Individual duration in nano seconds.
+  private ArrayList<Long> mResults = new ArrayList<>();
+
+  /**
+   * Calculates statistics.
+   */
+  private void calculateSatistics() {
+      final int size = mResults.size();
+      if (size <= 1) {
+          throw new IllegalStateException("At least two results are necessary.");
+      }
+
+      Collections.sort(mResults);
+      mMedian = size % 2 == 0 ?  (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
+              mResults.get(size / 2);
+
+      for (int i = 0; i < size; ++i) {
+          mMean += mResults.get(i);
+      }
+      mMean /= (double)size;
+
+      for (int i = 0; i < size; ++i) {
+          final double tmp = mResults.get(i) - mMean;
+          mStandardDeviation += tmp * tmp;
+      }
+      mStandardDeviation = Math.sqrt(mStandardDeviation / (double)(size - 1));
+  }
+
+  /**
+   * Judges whether the benchmark needs more samples.
+   *
+   * For the usage, see class comment.
+   */
+  public boolean keepRunning() {
+      switch (mState) {
+          case NOT_STARTED:
+              mNanoPreviousTime = System.nanoTime();
+              mNanoFinishTime = mNanoPreviousTime + mNanoTimeLimit;
+              mState = RUNNING;
+              return true;
+          case RUNNING:
+              final long currentTime = System.nanoTime();
+              mResults.add(currentTime - mNanoPreviousTime);
+
+              // To calculate statistics, needs two or more samples.
+              if (mResults.size() > 2 && currentTime > mNanoFinishTime) {
+                  calculateSatistics();
+                  mState = FINISHED;
+                  return false;
+              }
+
+              mNanoPreviousTime = currentTime;
+              return true;
+          case FINISHED:
+              throw new IllegalStateException("The benchmark has finished.");
+          default:
+              throw new IllegalStateException("The benchmark is in unknown state.");
+      }
+  }
+
+  public double mean() {
+      if (mState != FINISHED) {
+          throw new IllegalStateException("The benchmark hasn't finished");
+      }
+      return mMean;
+  }
+
+  public long median() {
+      if (mState != FINISHED) {
+          throw new IllegalStateException("The benchmark hasn't finished");
+      }
+      return mMedian;
+  }
+
+  public double standardDeviation() {
+      if (mState != FINISHED) {
+          throw new IllegalStateException("The benchmark hasn't finished");
+      }
+      return mStandardDeviation;
+  }
+
+  public String summaryLine() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("Summary: ");
+      sb.append("median=" + median() + "ns, ");
+      sb.append("mean=" + mean() + "ns, ");
+      sb.append("sigma=" + standardDeviation() + ", ");
+      sb.append("iteration=" + mResults.size());
+      return sb.toString();
+  }
+
+}
diff --git a/core/tests/perftests/src/android/widget/StubActivity.java b/core/tests/perftests/src/android/widget/StubActivity.java
new file mode 100644 (file)
index 0000000..f821d77
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.widget;
+
+import android.app.Activity;
+
+public class StubActivity extends Activity {
+}
diff --git a/core/tests/perftests/src/android/widget/TextViewSetTextLocalePerfTest.java b/core/tests/perftests/src/android/widget/TextViewSetTextLocalePerfTest.java
new file mode 100644 (file)
index 0000000..fecb2d8
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 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.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import android.perftest.BenchmarkState;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.Locale;
+import java.util.Collection;
+import java.util.Arrays;
+
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(Parameterized.class)
+public class TextViewSetTextLocalePerfTest {
+
+    @Parameters
+    public static Collection locales() {
+        return Arrays.asList(new Object[][] {
+            { "TextView_setTextLocale_SameLocale", "en-US", "en-US" },
+            { "TextView_setTextLocale_DifferentLocale", "en-US", "ja-JP"}
+        });
+    }
+
+    private String mMetricKey;
+    private Locale mFirstLocale;
+    private Locale mSecondLocale;
+
+    public TextViewSetTextLocalePerfTest(
+            String metricKey, String firstLocale, String secondLocale) {
+        mMetricKey = metricKey;
+        mFirstLocale = Locale.forLanguageTag(firstLocale);
+        mSecondLocale = Locale.forLanguageTag(secondLocale);
+    }
+
+    @Rule
+    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+
+    @Test
+    public void testSetTextLocale() {
+        TextView textView = new TextView(mActivityRule.getActivity());
+
+        BenchmarkState state = new BenchmarkState();
+
+        while (state.keepRunning()) {
+            textView.setTextLocale(mFirstLocale);
+            textView.setTextLocale(mSecondLocale);
+        }
+
+        Log.i("TextViewSetTextLocalePerfTest", mMetricKey + ": " + state.summaryLine());
+        final Bundle status = new Bundle();
+        status.putLong(mMetricKey, state.median());
+        InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
+    }
+}