1373259f2960b2fa508e524a035818b390f99e574Diego Perez/* 2373259f2960b2fa508e524a035818b390f99e574Diego Perez * Copyright (C) 2016 The Android Open Source Project 3373259f2960b2fa508e524a035818b390f99e574Diego Perez * 4373259f2960b2fa508e524a035818b390f99e574Diego Perez * Licensed under the Apache License, Version 2.0 (the "License"); 5373259f2960b2fa508e524a035818b390f99e574Diego Perez * you may not use this file except in compliance with the License. 6373259f2960b2fa508e524a035818b390f99e574Diego Perez * You may obtain a copy of the License at 7373259f2960b2fa508e524a035818b390f99e574Diego Perez * 8373259f2960b2fa508e524a035818b390f99e574Diego Perez * http://www.apache.org/licenses/LICENSE-2.0 9373259f2960b2fa508e524a035818b390f99e574Diego Perez * 10373259f2960b2fa508e524a035818b390f99e574Diego Perez * Unless required by applicable law or agreed to in writing, software 11373259f2960b2fa508e524a035818b390f99e574Diego Perez * distributed under the License is distributed on an "AS IS" BASIS, 12373259f2960b2fa508e524a035818b390f99e574Diego Perez * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13373259f2960b2fa508e524a035818b390f99e574Diego Perez * See the License for the specific language governing permissions and 14373259f2960b2fa508e524a035818b390f99e574Diego Perez * limitations under the License. 15373259f2960b2fa508e524a035818b390f99e574Diego Perez */ 16373259f2960b2fa508e524a035818b390f99e574Diego Perez 17373259f2960b2fa508e524a035818b390f99e574Diego Perezpackage com.android.layoutlib.bridge.intensive.util.perf; 18373259f2960b2fa508e524a035818b390f99e574Diego Perez 19373259f2960b2fa508e524a035818b390f99e574Diego Perezimport com.android.layoutlib.bridge.intensive.util.TestUtils; 20373259f2960b2fa508e524a035818b390f99e574Diego Perez 21373259f2960b2fa508e524a035818b390f99e574Diego Perezimport org.junit.runners.model.Statement; 22373259f2960b2fa508e524a035818b390f99e574Diego Perez 23373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.io.File; 24373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.io.FileInputStream; 25373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.io.FileOutputStream; 26373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.io.IOException; 27373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.Arrays; 28373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.Random; 29373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.concurrent.Executors; 30373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.concurrent.ScheduledExecutorService; 31373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.concurrent.TimeUnit; 32373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.concurrent.atomic.AtomicBoolean; 33373259f2960b2fa508e524a035818b390f99e574Diego Perezimport java.util.function.Consumer; 34373259f2960b2fa508e524a035818b390f99e574Diego Perez 35373259f2960b2fa508e524a035818b390f99e574Diego Perezimport com.google.common.hash.HashCode; 36373259f2960b2fa508e524a035818b390f99e574Diego Perezimport com.google.common.hash.HashFunction; 37373259f2960b2fa508e524a035818b390f99e574Diego Perezimport com.google.common.hash.Hashing; 38373259f2960b2fa508e524a035818b390f99e574Diego Perez 39373259f2960b2fa508e524a035818b390f99e574Diego Perez/** 40373259f2960b2fa508e524a035818b390f99e574Diego Perez * JUnit {@link Statement} used to measure some statistics about the test method. 41373259f2960b2fa508e524a035818b390f99e574Diego Perez */ 42373259f2960b2fa508e524a035818b390f99e574Diego Perezpublic class TimedStatement extends Statement { 43373259f2960b2fa508e524a035818b390f99e574Diego Perez private static final int CALIBRATION_WARMUP_ITERATIONS = 50; 44373259f2960b2fa508e524a035818b390f99e574Diego Perez private static final int CALIBRATION_RUNS = 100; 45373259f2960b2fa508e524a035818b390f99e574Diego Perez 46373259f2960b2fa508e524a035818b390f99e574Diego Perez private static boolean sIsCalibrated; 47373259f2960b2fa508e524a035818b390f99e574Diego Perez private static double sCalibrated; 48373259f2960b2fa508e524a035818b390f99e574Diego Perez 49373259f2960b2fa508e524a035818b390f99e574Diego Perez private final Statement mStatement; 50373259f2960b2fa508e524a035818b390f99e574Diego Perez private final int mWarmUpIterations; 51373259f2960b2fa508e524a035818b390f99e574Diego Perez private final int mRuns; 52373259f2960b2fa508e524a035818b390f99e574Diego Perez private final Runtime mRuntime = Runtime.getRuntime(); 53373259f2960b2fa508e524a035818b390f99e574Diego Perez private final Consumer<TimedStatementResult> mCallback; 54373259f2960b2fa508e524a035818b390f99e574Diego Perez 55373259f2960b2fa508e524a035818b390f99e574Diego Perez TimedStatement(Statement statement, int warmUpIterations, int runs, 56373259f2960b2fa508e524a035818b390f99e574Diego Perez Consumer<TimedStatementResult> finishedCallback) { 57373259f2960b2fa508e524a035818b390f99e574Diego Perez mStatement = statement; 58373259f2960b2fa508e524a035818b390f99e574Diego Perez mWarmUpIterations = warmUpIterations; 59373259f2960b2fa508e524a035818b390f99e574Diego Perez mRuns = runs; 60373259f2960b2fa508e524a035818b390f99e574Diego Perez mCallback = finishedCallback; 61373259f2960b2fa508e524a035818b390f99e574Diego Perez } 62373259f2960b2fa508e524a035818b390f99e574Diego Perez 63373259f2960b2fa508e524a035818b390f99e574Diego Perez /** 64373259f2960b2fa508e524a035818b390f99e574Diego Perez * The calibrate method tries to do some work that involves IO, memory allocations and some 65373259f2960b2fa508e524a035818b390f99e574Diego Perez * operations on the randomly generated data to calibrate the speed of the machine with 66373259f2960b2fa508e524a035818b390f99e574Diego Perez * something that resembles the execution of a test case. 67373259f2960b2fa508e524a035818b390f99e574Diego Perez */ 68373259f2960b2fa508e524a035818b390f99e574Diego Perez private static void calibrateMethod() throws IOException { 69373259f2960b2fa508e524a035818b390f99e574Diego Perez File tmpFile = File.createTempFile("test", "file"); 70373259f2960b2fa508e524a035818b390f99e574Diego Perez Random rnd = new Random(); 71373259f2960b2fa508e524a035818b390f99e574Diego Perez HashFunction hashFunction = Hashing.sha512(); 72373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int i = 0; i < 5 + rnd.nextInt(5); i++) { 73373259f2960b2fa508e524a035818b390f99e574Diego Perez FileOutputStream stream = new FileOutputStream(tmpFile); 74373259f2960b2fa508e524a035818b390f99e574Diego Perez int bytes = 30000 + rnd.nextInt(60000); 75373259f2960b2fa508e524a035818b390f99e574Diego Perez byte[] buffer = new byte[bytes]; 76373259f2960b2fa508e524a035818b390f99e574Diego Perez 77373259f2960b2fa508e524a035818b390f99e574Diego Perez rnd.nextBytes(buffer); 78373259f2960b2fa508e524a035818b390f99e574Diego Perez byte acc = 0; 79373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int j = 0; j < bytes; j++) { 80373259f2960b2fa508e524a035818b390f99e574Diego Perez acc += buffer[i]; 81373259f2960b2fa508e524a035818b390f99e574Diego Perez } 82373259f2960b2fa508e524a035818b390f99e574Diego Perez buffer[0] = acc; 83373259f2960b2fa508e524a035818b390f99e574Diego Perez stream.write(buffer); 84373259f2960b2fa508e524a035818b390f99e574Diego Perez System.gc(); 85373259f2960b2fa508e524a035818b390f99e574Diego Perez stream.close(); 86373259f2960b2fa508e524a035818b390f99e574Diego Perez FileInputStream input = new FileInputStream(tmpFile); 87373259f2960b2fa508e524a035818b390f99e574Diego Perez byte[] readBuffer = new byte[bytes]; 88373259f2960b2fa508e524a035818b390f99e574Diego Perez //noinspection ResultOfMethodCallIgnored 89373259f2960b2fa508e524a035818b390f99e574Diego Perez input.read(readBuffer); 90373259f2960b2fa508e524a035818b390f99e574Diego Perez buffer = readBuffer; 91373259f2960b2fa508e524a035818b390f99e574Diego Perez HashCode code1 = hashFunction.hashBytes(buffer); 92373259f2960b2fa508e524a035818b390f99e574Diego Perez Arrays.sort(buffer); 93373259f2960b2fa508e524a035818b390f99e574Diego Perez HashCode code2 = hashFunction.hashBytes(buffer); 94373259f2960b2fa508e524a035818b390f99e574Diego Perez input.close(); 95373259f2960b2fa508e524a035818b390f99e574Diego Perez 96373259f2960b2fa508e524a035818b390f99e574Diego Perez FileOutputStream hashStream = new FileOutputStream(tmpFile); 97373259f2960b2fa508e524a035818b390f99e574Diego Perez hashStream.write(code1.asBytes()); 98373259f2960b2fa508e524a035818b390f99e574Diego Perez hashStream.write(code2.asBytes()); 99373259f2960b2fa508e524a035818b390f99e574Diego Perez hashStream.close(); 100373259f2960b2fa508e524a035818b390f99e574Diego Perez } 101373259f2960b2fa508e524a035818b390f99e574Diego Perez } 102373259f2960b2fa508e524a035818b390f99e574Diego Perez 103373259f2960b2fa508e524a035818b390f99e574Diego Perez /** 104373259f2960b2fa508e524a035818b390f99e574Diego Perez * Runs the calibration process and sets the calibration measure in {@link #sCalibrated} 105373259f2960b2fa508e524a035818b390f99e574Diego Perez */ 106373259f2960b2fa508e524a035818b390f99e574Diego Perez private static void doCalibration() throws IOException { 107373259f2960b2fa508e524a035818b390f99e574Diego Perez System.out.println("Calibrating ..."); 108373259f2960b2fa508e524a035818b390f99e574Diego Perez TestUtils.gc(); 109373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int i = 0; i < CALIBRATION_WARMUP_ITERATIONS; i++) { 110373259f2960b2fa508e524a035818b390f99e574Diego Perez calibrateMethod(); 111373259f2960b2fa508e524a035818b390f99e574Diego Perez } 112373259f2960b2fa508e524a035818b390f99e574Diego Perez 113373259f2960b2fa508e524a035818b390f99e574Diego Perez LongStatsCollector stats = new LongStatsCollector(CALIBRATION_RUNS); 114373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int i = 0; i < CALIBRATION_RUNS; i++) { 115373259f2960b2fa508e524a035818b390f99e574Diego Perez TestUtils.gc(); 116373259f2960b2fa508e524a035818b390f99e574Diego Perez long start = System.currentTimeMillis(); 117373259f2960b2fa508e524a035818b390f99e574Diego Perez calibrateMethod(); 118373259f2960b2fa508e524a035818b390f99e574Diego Perez stats.accept(System.currentTimeMillis() - start); 119373259f2960b2fa508e524a035818b390f99e574Diego Perez } 120373259f2960b2fa508e524a035818b390f99e574Diego Perez 121373259f2960b2fa508e524a035818b390f99e574Diego Perez sCalibrated = stats.getStats().getMedian(); 122373259f2960b2fa508e524a035818b390f99e574Diego Perez sIsCalibrated = true; 123373259f2960b2fa508e524a035818b390f99e574Diego Perez System.out.printf(" DONE %fms\n", sCalibrated); 124373259f2960b2fa508e524a035818b390f99e574Diego Perez } 125373259f2960b2fa508e524a035818b390f99e574Diego Perez 126373259f2960b2fa508e524a035818b390f99e574Diego Perez private long getUsedMemory() { 127373259f2960b2fa508e524a035818b390f99e574Diego Perez return mRuntime.totalMemory() - mRuntime.freeMemory(); 128373259f2960b2fa508e524a035818b390f99e574Diego Perez } 129373259f2960b2fa508e524a035818b390f99e574Diego Perez 130373259f2960b2fa508e524a035818b390f99e574Diego Perez 131373259f2960b2fa508e524a035818b390f99e574Diego Perez @Override 132373259f2960b2fa508e524a035818b390f99e574Diego Perez public void evaluate() throws Throwable { 133373259f2960b2fa508e524a035818b390f99e574Diego Perez if (!sIsCalibrated) { 134373259f2960b2fa508e524a035818b390f99e574Diego Perez doCalibration(); 135373259f2960b2fa508e524a035818b390f99e574Diego Perez } 136373259f2960b2fa508e524a035818b390f99e574Diego Perez 137373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int i = 0; i < mWarmUpIterations; i++) { 138373259f2960b2fa508e524a035818b390f99e574Diego Perez mStatement.evaluate(); 139373259f2960b2fa508e524a035818b390f99e574Diego Perez } 140373259f2960b2fa508e524a035818b390f99e574Diego Perez 141373259f2960b2fa508e524a035818b390f99e574Diego Perez LongStatsCollector timeStats = new LongStatsCollector(mRuns); 142373259f2960b2fa508e524a035818b390f99e574Diego Perez LongStatsCollector memoryUseStats = new LongStatsCollector(mRuns); 143373259f2960b2fa508e524a035818b390f99e574Diego Perez AtomicBoolean collectSamples = new AtomicBoolean(false); 144373259f2960b2fa508e524a035818b390f99e574Diego Perez 145373259f2960b2fa508e524a035818b390f99e574Diego Perez ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 146373259f2960b2fa508e524a035818b390f99e574Diego Perez TestUtils.gc(); 147373259f2960b2fa508e524a035818b390f99e574Diego Perez executorService.scheduleAtFixedRate(() -> { 148373259f2960b2fa508e524a035818b390f99e574Diego Perez if (!collectSamples.get()) { 149373259f2960b2fa508e524a035818b390f99e574Diego Perez return; 150373259f2960b2fa508e524a035818b390f99e574Diego Perez } 151373259f2960b2fa508e524a035818b390f99e574Diego Perez memoryUseStats.accept(getUsedMemory()); 152373259f2960b2fa508e524a035818b390f99e574Diego Perez }, 0, 200, TimeUnit.MILLISECONDS); 153373259f2960b2fa508e524a035818b390f99e574Diego Perez 154373259f2960b2fa508e524a035818b390f99e574Diego Perez try { 155373259f2960b2fa508e524a035818b390f99e574Diego Perez for (int i = 0; i < mRuns; i++) { 156373259f2960b2fa508e524a035818b390f99e574Diego Perez TestUtils.gc(); 157373259f2960b2fa508e524a035818b390f99e574Diego Perez collectSamples.set(true); 158373259f2960b2fa508e524a035818b390f99e574Diego Perez long startTimeMs = System.currentTimeMillis(); 159373259f2960b2fa508e524a035818b390f99e574Diego Perez mStatement.evaluate(); 160373259f2960b2fa508e524a035818b390f99e574Diego Perez long stopTimeMs = System.currentTimeMillis(); 161373259f2960b2fa508e524a035818b390f99e574Diego Perez collectSamples.set(true); 162373259f2960b2fa508e524a035818b390f99e574Diego Perez timeStats.accept(stopTimeMs - startTimeMs); 163373259f2960b2fa508e524a035818b390f99e574Diego Perez 164373259f2960b2fa508e524a035818b390f99e574Diego Perez } 165373259f2960b2fa508e524a035818b390f99e574Diego Perez } finally { 166373259f2960b2fa508e524a035818b390f99e574Diego Perez executorService.shutdownNow(); 167373259f2960b2fa508e524a035818b390f99e574Diego Perez } 168373259f2960b2fa508e524a035818b390f99e574Diego Perez 169373259f2960b2fa508e524a035818b390f99e574Diego Perez TimedStatementResult result = new TimedStatementResult( 170373259f2960b2fa508e524a035818b390f99e574Diego Perez mWarmUpIterations, 171373259f2960b2fa508e524a035818b390f99e574Diego Perez mRuns, 172373259f2960b2fa508e524a035818b390f99e574Diego Perez sCalibrated, 173373259f2960b2fa508e524a035818b390f99e574Diego Perez timeStats.getStats(), 174373259f2960b2fa508e524a035818b390f99e574Diego Perez memoryUseStats.getStats()); 175373259f2960b2fa508e524a035818b390f99e574Diego Perez mCallback.accept(result); 176373259f2960b2fa508e524a035818b390f99e574Diego Perez } 177373259f2960b2fa508e524a035818b390f99e574Diego Perez 178373259f2960b2fa508e524a035818b390f99e574Diego Perez} 179