BenchmarkState.java revision a9cebd628c53b900d48600a5e7ced546c91522e6
1eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad/* 2eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Copyright (C) 2016 The Android Open Source Project 3eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 4eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Licensed under the Apache License, Version 2.0 (the "License"); 5eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * you may not use this file except in compliance with the License. 6eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * You may obtain a copy of the License at 7eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 8eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * http://www.apache.org/licenses/LICENSE-2.0 9eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 10eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Unless required by applicable law or agreed to in writing, software 11eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * distributed under the License is distributed on an "AS IS" BASIS, 12eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * See the License for the specific language governing permissions and 14eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * limitations under the License. 15eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad */ 16eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 17eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadpackage android.perftests.utils; 18eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 19eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport android.app.Activity; 20eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport android.app.Instrumentation; 21eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport android.os.Bundle; 22eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport android.util.Log; 23eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 24eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport java.util.ArrayList; 25eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadimport java.util.Collections; 26eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 27eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad/** 28eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Provides a benchmark framework. 29eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 30eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Example usage: 31eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * // Executes the code while keepRunning returning true. 32eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 33eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * public void sampleMethod() { 34eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * BenchmarkState state = new BenchmarkState(); 35eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 36eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 37eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * while (state.keepRunning()) { 38eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * int[] dest = new int[src.length]; 39eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * System.arraycopy(src, 0, dest, 0, src.length); 40eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * } 41eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * System.out.println(state.summaryLine()); 42eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * } 43eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad */ 44eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstadpublic class BenchmarkState { 45eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private static final String TAG = "BenchmarkState"; 46eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 47eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private static final int NOT_STARTED = 1; // The benchmark has not started yet. 48eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private static final int RUNNING = 2; // The benchmark is running. 49eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private static final int FINISHED = 3; // The benchmark has stopped. 50eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private static final int MIN_REPEAT_TIMES = 16; 51eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 52eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private int mState = NOT_STARTED; // Current benchmark state. 53eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 54eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private long mNanoPreviousTime = 0; // Previously captured System.nanoTime(). 55eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private long mNanoFinishTime = 0; // Finish if System.nanoTime() returns after than this value. 56eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private long mNanoTimeLimit = 1 * 1000 * 1000 * 1000; // 1 sec. Default time limit. 57eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 58eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad // Statistics. These values will be filled when the benchmark has finished. 59eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad // The computation needs double precision, but long int is fine for final reporting. 60eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private long mMedian = 0; 61eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private double mMean = 0.0; 62eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private double mStandardDeviation = 0.0; 63eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 64eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad // Individual duration in nano seconds. 65eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private ArrayList<Long> mResults = new ArrayList<>(); 66eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 67eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad /** 68eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Calculates statistics. 69eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad */ 70eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private void calculateSatistics() { 71eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad final int size = mResults.size(); 72eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad if (size <= 1) { 73eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("At least two results are necessary."); 74eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 75eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 76eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad Collections.sort(mResults); 77eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 : 78eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mResults.get(size / 2); 79eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 80eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad for (int i = 0; i < size; ++i) { 81eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mMean += mResults.get(i); 82eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 83eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mMean /= (double) size; 84eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 85eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad for (int i = 0; i < size; ++i) { 86eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad final double tmp = mResults.get(i) - mMean; 87eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mStandardDeviation += tmp * tmp; 88eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 89eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1)); 90eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 91eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 92eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad /** 93eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * Judges whether the benchmark needs more samples. 94eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * 95eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad * For the usage, see class comment. 96eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad */ 97eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad public boolean keepRunning() { 98eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad switch (mState) { 99eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad case NOT_STARTED: 100eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mNanoPreviousTime = System.nanoTime(); 101eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mNanoFinishTime = mNanoPreviousTime + mNanoTimeLimit; 102eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mState = RUNNING; 103eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return true; 104eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad case RUNNING: 105eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad final long currentTime = System.nanoTime(); 106eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mResults.add(currentTime - mNanoPreviousTime); 107eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 108eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad // To calculate statistics, needs two or more samples. 109eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad if (mResults.size() > MIN_REPEAT_TIMES && currentTime > mNanoFinishTime) { 110eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad calculateSatistics(); 111eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mState = FINISHED; 112eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return false; 113eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 114eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 115eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad mNanoPreviousTime = currentTime; 116eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return true; 117eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad case FINISHED: 118eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("The benchmark has finished."); 119eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad default: 120eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("The benchmark is in unknown state."); 121eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 122eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 123eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 124eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad public long mean() { 125eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad if (mState != FINISHED) { 126eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("The benchmark hasn't finished"); 127eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 128eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return (long) mMean; 129eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 130eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 131eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad public long median() { 132eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad if (mState != FINISHED) { 133eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("The benchmark hasn't finished"); 134eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 135eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return mMedian; 136eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 137eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 138eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad public long standardDeviation() { 139eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad if (mState != FINISHED) { 140eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad throw new IllegalStateException("The benchmark hasn't finished"); 141eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 142eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return (long) mStandardDeviation; 143eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 144eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 145eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad private String summaryLine() { 146eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad StringBuilder sb = new StringBuilder(); 147eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("Summary: "); 148eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("median=").append(median()).append("ns, "); 149eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("mean=").append(mean()).append("ns, "); 150eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("sigma=").append(standardDeviation()).append(", "); 151eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("iteration=").append(mResults.size()).append(", "); 152eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad // print out the first few iterations' number for double checking. 153eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad int sampleNumber = Math.min(mResults.size(), MIN_REPEAT_TIMES); 154eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad for (int i = 0; i < sampleNumber; i++) { 155eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad sb.append("No ").append(i).append(" result is ").append(mResults.get(i)).append(", "); 156eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 157eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad return sb.toString(); 158eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 159eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad 160eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad public void sendFullStatusReport(Instrumentation instrumentation, String key) { 161eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad Log.i(TAG, key + summaryLine()); 162eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad Bundle status = new Bundle(); 163eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad status.putLong(key + "_median", median()); 164eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad status.putLong(key + "_mean", mean()); 165eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad status.putLong(key + "_standardDeviation", standardDeviation()); 166eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad instrumentation.sendStatus(Activity.RESULT_OK, status); 167eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad } 168eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad} 169eabc9e95768e7ac9acc3b32dc9ac2edf99c9e2c5Mårten Kongstad