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