Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62e5fea8 authored by John Reck's avatar John Reck
Browse files

Greatly reduce BenchmarkState overhead

CL reduces BenchmarkState to minimal levels. It also adds
a warmup loop to get things going first before starting measurements.

With this change with clocks /not/ locked on bullhead the test for
RenderNodeJniOverhead is showing a stable (0ns std dev) result
of 54ns, which is approximately the expected amount.

Test: Ran a few perf benchmarks

Change-Id: If01e455884711ebd9cfb89f076efa19dc0b5436d
parent ccbc5c18
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -37,4 +37,10 @@ public class SystemPerfTest {
            System.nanoTime();
        }
    }

    @Test
    public void testBenchmarkOverhead() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {}
    }
}
+1 −4
Original line number Diff line number Diff line
@@ -57,8 +57,6 @@ import java.util.concurrent.TimeUnit;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class UserLifecycleTest {
    private final int MIN_REPEAT_TIMES = 4;

    private final int TIMEOUT_REMOVE_USER_MS = 4 * 1000; // 4 sec
    private final int CHECK_USER_REMOVED_INTERVAL_MS = 200; // 0.2 sec

@@ -90,7 +88,6 @@ public class UserLifecycleTest {
        mAm = context.getSystemService(ActivityManager.class);
        mIam = ActivityManagerNative.getDefault();
        mState = mPerfStatusReporter.getBenchmarkState();
        mState.setMinRepeatTimes(MIN_REPEAT_TIMES);
        mUsersToRemove = new ArrayList<>();
    }

+91 −37
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.util.Log;

import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * Provides a benchmark framework.
@@ -41,39 +42,47 @@ import java.util.Collections;
 *     System.out.println(state.summaryLine());
 * }
 */
public class BenchmarkState {
public final class BenchmarkState {
    private static final String TAG = "BenchmarkState";

    private static final int NOT_STARTED = 1;  // The benchmark has not started yet.
    private static final int NOT_STARTED = 0;  // The benchmark has not started yet.
    private static final int WARMUP = 1; // The benchmark is warming up.
    private static final int RUNNING = 2;  // The benchmark is running.
    private static final int RUNNING_PAUSED = 3;  // The benchmark is temporary paused.
    private static final int FINISHED = 4;  // 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 mNanoPausedTime = 0; // The System.nanoTime() when the pauseTiming() is called.
    private long mNanoPausedDuration = 0;  // The duration of paused state in nano sec.
    private long mNanoTimeLimit = 1 * 1000 * 1000 * 1000;  // 1 sec. Default time limit.
    private static final long WARMUP_DURATION_NS = ms2ns(250); // warm-up for at least 250ms
    private static final int WARMUP_MIN_ITERATIONS = 16; // minimum iterations to warm-up for

    // TODO: Tune these values.
    private static final long TARGET_TEST_DURATION_NS = ms2ns(500); // target testing for 500 ms
    private static final int MAX_TEST_ITERATIONS = 1000000;
    private static final int MIN_TEST_ITERATIONS = 100;
    private static final int REPEAT_COUNT = 5;

    private long mStartTimeNs = 0;  // Previously captured System.nanoTime().
    private long mPausedTimeNs = 0; // The System.nanoTime() when the pauseTiming() is called.
    private long mPausedDurationNs = 0;  // The duration of paused state in nano sec.

    private int mIteration = 0;
    private int mMaxIterations = 0;

    private int mRepeatCount = 0;

    // 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 long mMedian = 0;
    private double mMean = 0.0;
    private double mStandardDeviation = 0.0;

    // Number of iterations needed for calculating the stats.
    private int mMinRepeatTimes = 16;
    private long mMin = 0;

    // Individual duration in nano seconds.
    private ArrayList<Long> mResults = new ArrayList<>();

    /**
     * Sets the number of iterations needed for calculating the stats. Default is 16.
     */
    public void setMinRepeatTimes(int minRepeatTimes) {
        mMinRepeatTimes = minRepeatTimes;
    private static final long ms2ns(long ms) {
        return TimeUnit.MILLISECONDS.toNanos(ms);
    }

    /**
@@ -89,8 +98,13 @@ public class BenchmarkState {
        mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
                mResults.get(size / 2);

        mMin = mResults.get(0);
        for (int i = 0; i < size; ++i) {
            mMean += mResults.get(i);
            long result = mResults.get(i);
            mMean += result;
            if (result < mMin) {
                mMin = result;
            }
        }
        mMean /= (double) size;

@@ -108,7 +122,7 @@ public class BenchmarkState {
            throw new IllegalStateException(
                    "Unable to pause the benchmark. The benchmark has already paused.");
        }
        mNanoPausedTime = System.nanoTime();
        mPausedTimeNs = System.nanoTime();
        mState = RUNNING_PAUSED;
    }

@@ -119,9 +133,41 @@ public class BenchmarkState {
            throw new IllegalStateException(
                    "Unable to resume the benchmark. The benchmark is already running.");
        }
        mNanoPausedDuration += System.nanoTime() - mNanoPausedTime;
        mNanoPausedTime = 0;
        mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
        mPausedTimeNs = 0;
        mState = RUNNING;
    }

    private void beginWarmup() {
        mStartTimeNs = System.nanoTime();
        mIteration = 0;
        mState = WARMUP;
    }

    private void beginBenchmark(long warmupDuration, int iterations) {
        mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
        mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
                Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
        mPausedDurationNs = 0;
        mIteration = 0;
        mRepeatCount = 0;
        mState = RUNNING;
        mStartTimeNs = System.nanoTime();
    }

    private boolean startNextTestRun() {
        final long currentTime = System.nanoTime();
        mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
        mRepeatCount++;
        if (mRepeatCount >= REPEAT_COUNT) {
            calculateSatistics();
            mState = FINISHED;
            return false;
        }
        mPausedDurationNs = 0;
        mIteration = 0;
        mStartTimeNs = System.nanoTime();
        return true;
    }

    /**
@@ -132,23 +178,22 @@ public class BenchmarkState {
    public boolean keepRunning() {
        switch (mState) {
            case NOT_STARTED:
                mNanoPreviousTime = System.nanoTime();
                mNanoFinishTime = mNanoPreviousTime + mNanoTimeLimit;
                mState = RUNNING;
                beginWarmup();
                return true;
            case WARMUP:
                mIteration++;
                // Only check nanoTime on every iteration in WARMUP since we
                // don't yet have a target iteration count.
                final long duration = System.nanoTime() - mStartTimeNs;
                if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
                    beginBenchmark(duration, mIteration);
                }
                return true;
            case RUNNING:
                final long currentTime = System.nanoTime();
                mResults.add(currentTime - mNanoPreviousTime - mNanoPausedDuration);
                mNanoPausedDuration = 0;

                // To calculate statistics, needs two or more samples.
                if (mResults.size() > mMinRepeatTimes && currentTime > mNanoFinishTime) {
                    calculateSatistics();
                    mState = FINISHED;
                    return false;
                mIteration++;
                if (mIteration >= mMaxIterations) {
                    return startNextTestRun();
                }

                mNanoPreviousTime = currentTime;
                return true;
            case RUNNING_PAUSED:
                throw new IllegalStateException(
@@ -161,21 +206,28 @@ public class BenchmarkState {
        }
    }

    public long mean() {
    private long mean() {
        if (mState != FINISHED) {
            throw new IllegalStateException("The benchmark hasn't finished");
        }
        return (long) mMean;
    }

    public long median() {
    private long median() {
        if (mState != FINISHED) {
            throw new IllegalStateException("The benchmark hasn't finished");
        }
        return mMedian;
    }

    public long standardDeviation() {
    private long min() {
        if (mState != FINISHED) {
            throw new IllegalStateException("The benchmark hasn't finished");
        }
        return mMin;
    }

    private long standardDeviation() {
        if (mState != FINISHED) {
            throw new IllegalStateException("The benchmark hasn't finished");
        }
@@ -187,10 +239,11 @@ public class BenchmarkState {
        sb.append("Summary: ");
        sb.append("median=").append(median()).append("ns, ");
        sb.append("mean=").append(mean()).append("ns, ");
        sb.append("min=").append(min()).append("ns, ");
        sb.append("sigma=").append(standardDeviation()).append(", ");
        sb.append("iteration=").append(mResults.size()).append(", ");
        // print out the first few iterations' number for double checking.
        int sampleNumber = Math.min(mResults.size(), mMinRepeatTimes);
        int sampleNumber = Math.min(mResults.size(), 16);
        for (int i = 0; i < sampleNumber; i++) {
            sb.append("No ").append(i).append(" result is ").append(mResults.get(i)).append(", ");
        }
@@ -202,6 +255,7 @@ public class BenchmarkState {
        Bundle status = new Bundle();
        status.putLong(key + "_median", median());
        status.putLong(key + "_mean", mean());
        status.putLong(key + "_min", min());
        status.putLong(key + "_standardDeviation", standardDeviation());
        instrumentation.sendStatus(Activity.RESULT_OK, status);
    }