Java tutorial
/* dsh-timer Timer with nanosecond resolution and summary statistics on recorded elapsed times. Copyright (c) 2004-2013 held jointly by the individual authors. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. > http://www.fsf.org/licensing/licenses/lgpl.html > http://www.opensource.org/licenses/lgpl-license.php */ package org.dishevelled.timer; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.Random; import java.util.HashMap; import java.util.Collections; import org.apache.commons.math.stat.descriptive.SummaryStatistics; /** * Timer class with nanosecond resolution and summary * statistics on recorded elapsed times. This class provides * nanosecond precision but not necessarily nanosecond accuracy * (see the javadoc for <code>System.nanoTime()</code> for more * details). The recorded times themselves are not preserved, * however, only the statistics. As a consequence <code>start()</code> * and <code>stop()</code> can be called many number of times * without requiring large amounts of memory. * * <h4>Special cases</h4> * <p>When <code>size() == 0</code>, * <pre> * min() == Double.NaN * max() == Double.NaN * mean() == Double.NaN * standardDeviation() == Double.NaN * </pre> * </p> * <p>When <code>size() == 1</code>, * <pre> * standardDeviation() == 0.0d * </pre> * </p> * * <h4>Static methods</h4> * <p>Execution time can be sensitive to various factors, such * as order of execution, runtime optimization from the just-in-time compiler * (JIT), and garbage collection. This class provides static methods * to help deal with these factors.</p> * * <p>Given a few benchmarks to run, wrap them in Runnable objects * <pre> * Runnable r0 = new Runnable() { public void run() { ... } }; * Runnable r1 = new Runnable() { public void run() { ... } }; * Runnable r2 = new Runnable() { public void run() { ... } }; * List<Runnable> benchmarks = Arrays.asList(new Runnable[] { r0, r1, r2 }); * </pre> * Prime the JIT by running the benchmarks several times * <pre> * Timer.prime(benchmarks, 1000); * </pre> * Then measure the execution times of the benchmarks by running * them several times in random execution order * <pre> * Map<Runnable, Timer> result = Timer.shuffle(benchmarks, 100, 100); * </pre> * Summary statistics on recorded execution times are captured by the * timer returned for each Runnable benchmark * <pre> * for (Map.Entry<Runnable, Timer> e : result.entrySet()) { * Runnable r = e.getKey(); * Timer t = e.getValue(); * System.out.println("runnable=" + r + " mean execution time=" + t.mean() + "ns"); * } * </pre></p> * * @see java.lang.System#nanoTime * @author Michael Heuer */ public final class Timer { /** Summary statistics. */ private final SummaryStatistics summaryStatistics; /** Last start time, in nanoseconds. */ private double startTime; /** Flag indicating this timer has been started at least once. */ private boolean started; /** * Create a new timer. */ public Timer() { summaryStatistics = new SummaryStatistics(); started = false; } /** * Reset the record of elapsed times from this timer. After * the timer has been reset, it must be started at least once * before <code>stop()</code> is called. */ public void reset() { summaryStatistics.clear(); started = false; } /** * Start the timer. The timer must be started at least once * before <code>stop()</code> is called. */ public void start() { started = true; startTime = System.nanoTime(); } /** * Stop the timer and record the time elapsed in nanoseconds * since <code>start()</code> was last called. * * @throws TimerException if <code>start()</code> has not been called * at least once before this method was called */ public void stop() { if (started) { double currentTime = System.nanoTime(); double elapsedTime = currentTime - startTime; summaryStatistics.addValue(elapsedTime); } else { throw new TimerException("timer was never started"); } } /** * Return the minimum elapsed time recorded by this timer * in nanoseconds. * * @return the minimum elapsed time recorded by this timer * in nanoseconds */ public double min() { return summaryStatistics.getMin(); } /** * Return the maximum elapsed time recorded by this timer * in nanoseconds. * * @return the maximum elapsed time recorded by this timer * in nanoseconds */ public double max() { return summaryStatistics.getMax(); } /** * Return the number of elapsed times recorded by this timer. * * @return the number of elapsed times recorded by this timer */ public long size() { return summaryStatistics.getN(); } /** * Return the sum of the elapsed times recorded by this timer * in nanoseconds. * * @return the sum of the elapsed times recorded by this timer */ public double sum() { return summaryStatistics.getSum(); } /** * Return the arithmetic mean of the elapsed times recorded by this * timer in nanoseconds. * * @return the arithmetic mean of the elapsed times recorded by this * timer in nanoseconds */ public double mean() { return summaryStatistics.getMean(); } /** * Return the standard deviation of the elapsed times recorded by this * timer in nanoseconds. * * @return the standard deviation of the elapsed times recorded by this * timer in nanoseconds */ public double standardDeviation() { return summaryStatistics.getStandardDeviation(); } /** * Time the specified code block. * * @param codeBlock code block to execute * @return timer used to measure execution time */ public static Timer time(final Runnable codeBlock) { return time(codeBlock, new Timer()); } /** * Time the specified code block with the specified timer. * * @param codeBlock code block to execute * @param t timer to use to measure execution time * @return timer used to measure execution time */ public static Timer time(final Runnable codeBlock, final Timer t) { t.start(); codeBlock.run(); t.stop(); return t; } /** * Prime the just-in-time compiler (JIT) by executing the * specified code block <code>n</code> times. * * @param codeBlock code block to execute * @param n number of times to execute code block */ public static void prime(final Runnable codeBlock, final int n) { for (int i = 0; i < n; i++) { codeBlock.run(); } } /** * Prime the just-in-time compiler (JIT) by executing each * code block in the specified list of code blocks <code>n</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to execute each code block */ public static void prime(final List<? extends Runnable> codeBlocks, final int n) { for (Runnable codeBlock : codeBlocks) { prime(codeBlock, n); } } /** * Loop over the specified code block <code>n</code> times. * * @param codeBlock code block to execute * @param n number of times to execute code block * @return timer used to measure execution time */ public static Timer loop(final Runnable codeBlock, final int n) { return loop(codeBlock, n, new Timer()); } /** * Loop over the specified code block <code>n</code> times * with the specified timer. * * @param codeBlock code block to execute * @param n number of times to execute code block * @param t timer to use to measure execution time * @return timer used to measure execution time */ public static Timer loop(final Runnable codeBlock, final int n, final Timer t) { for (int i = 0; i < n; i++) { time(codeBlock, t); } return t; } /** * For each of the code blocks in the specified list of code blocks, * loop over the code block <code>n</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to execute each code block * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> loop(final List<? extends Runnable> codeBlocks, final int n) { Map<Runnable, Timer> map = new HashMap<Runnable, Timer>(codeBlocks.size()); for (Runnable codeBlock : codeBlocks) { map.put(codeBlock, loop(codeBlock, n)); } return Collections.unmodifiableMap(map); } /** * Loop over the code blocks in the specified list of code blocks * <code>n</code> times, executing each code block <code>m</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to loop over the list of code blocks * @param m number of times to execute each code block * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> loop(final List<? extends Runnable> codeBlocks, final int n, final int m) { Map<Runnable, Timer> map = new HashMap<Runnable, Timer>(codeBlocks.size()); for (int i = 0; i < n; i++) { for (Runnable codeBlock : codeBlocks) { if (map.containsKey(codeBlock)) { Timer t = map.get(codeBlock); loop(codeBlock, m, t); map.put(codeBlock, t); } else { map.put(codeBlock, loop(codeBlock, m)); } } } return Collections.unmodifiableMap(map); } /** * For each of the code blocks in the specified list of code blocks, * executed in random order, loop over the code block <code>n</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to execute each code block * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> shuffle(final List<? extends Runnable> codeBlocks, final int n) { return shuffle(codeBlocks, n, new Random()); } /** * For each of the code blocks in the specified list of code blocks, * executed in random order using the specified source of randomness, * loop over the code block <code>n</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to execute each code block * @param random source of randomness * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> shuffle(final List<? extends Runnable> codeBlocks, final int n, final Random random) { List<Runnable> codeBlocksCopy = new ArrayList<Runnable>(codeBlocks); Collections.shuffle(codeBlocksCopy, random); return loop(codeBlocksCopy, n); } /** * Loop over the code blocks in the specified list of code blocks * <code>n</code> times, in random order, executing each code block * <code>m</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to loop over the list of code blocks * @param m number of times to execute each code block * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> shuffle(final List<? extends Runnable> codeBlocks, final int n, final int m) { return shuffle(codeBlocks, n, m, new Random()); } /** * Loop over the code blocks in the specified list of code blocks * <code>n</code> times, in random order using the specified source of * randomness, executing each code block <code>m</code> times. * * @param codeBlocks list of code blocks to execute * @param n number of times to loop over the list of code blocks * @param m number of times to execute each code block * @param random source of randomness * @return map of code blocks to timers used to measure execution time */ public static Map<Runnable, Timer> shuffle(final List<? extends Runnable> codeBlocks, final int n, final int m, final Random random) { List<Runnable> codeBlocksCopy = new ArrayList<Runnable>(codeBlocks); Map<Runnable, Timer> map = new HashMap<Runnable, Timer>(codeBlocksCopy.size()); for (int i = 0; i < n; i++) { Collections.shuffle(codeBlocksCopy, random); for (Runnable codeBlock : codeBlocksCopy) { if (map.containsKey(codeBlock)) { Timer t = map.get(codeBlock); loop(codeBlock, m, t); map.put(codeBlock, t); } else { map.put(codeBlock, loop(codeBlock, m)); } } } return Collections.unmodifiableMap(map); } }