1c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev/* 2c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * Copyright (C) 2008 The Guava Authors 3c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * 4c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * Licensed under the Apache License, Version 2.0 (the "License"); 5c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * you may not use this file except in compliance with the License. 6c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * You may obtain a copy of the License at 7c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * 8c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * http://www.apache.org/licenses/LICENSE-2.0 9c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * 10c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * Unless required by applicable law or agreed to in writing, software 11c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * distributed under the License is distributed on an "AS IS" BASIS, 12c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * See the License for the specific language governing permissions and 14c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * limitations under the License. 15c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev */ 16c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev 17c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevpackage com.google.common.base; 182275b79621e8d22c2a36f95cdaa04e8653a56721Matt Beaumont-Gay 19c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static com.google.common.base.Preconditions.checkNotNull; 20c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static com.google.common.base.Preconditions.checkState; 21c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static java.util.concurrent.TimeUnit.MICROSECONDS; 22c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static java.util.concurrent.TimeUnit.MILLISECONDS; 23c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static java.util.concurrent.TimeUnit.NANOSECONDS; 24c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataevimport static java.util.concurrent.TimeUnit.SECONDS; 25c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev 26ef8225444452a1486bd721f3285301fe84643b00Stephen Hinesimport com.google.common.annotations.Beta; 27ef8225444452a1486bd721f3285301fe84643b00Stephen Hinesimport com.google.common.annotations.GwtCompatible; 28c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev 296bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hinesimport java.util.concurrent.TimeUnit; 30c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev 31c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev/** 324fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * An object that measures elapsed time in nanoseconds. It is useful to measure 334fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * elapsed time using this class instead of direct calls to {@link 344fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * System#nanoTime} for a few reasons: 354fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * 364fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * <ul> 374fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * <li>An alternate time source can be substituted, for testing or performance 386bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * reasons. 394fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * <li>As documented by {@code nanoTime}, the value returned has no absolute 404fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * meaning, and can only be interpreted as relative to another timestamp 414fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * returned by {@code nanoTime} at a different time. {@code Stopwatch} is a 424fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * more effective abstraction because it exposes only these relative values, 434fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * not the absolute ones. 444fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * </ul> 454fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * 466bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * <p>Basic usage: 476bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * <pre> 486bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * Stopwatch stopwatch = new Stopwatch().{@link #start start}(); 496bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * doSomething(); 506bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * stopwatch.{@link #stop stop}(); // optional 516bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * 526bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * long millis = stopwatch.{@link #elapsedMillis elapsedMillis}(); 536bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * 546bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines * log.info("that took: " + stopwatch); // formatted string like "12.3 ms" 554fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * </pre> 564fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * 57ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * <p>Stopwatch methods are not idempotent; it is an error to start or stop a 58ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * stopwatch that is already in the desired state. 59ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * 60ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * <p>When testing code that uses this class, use the {@linkplain 61ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * #Stopwatch(Ticker) alternate constructor} to supply a fake or mock ticker. 62ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * <!-- TODO(kevinb): restore the "such as" --> This allows you to 63ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * simulate any valid behavior of the stopwatch. 64ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * 652275b79621e8d22c2a36f95cdaa04e8653a56721Matt Beaumont-Gay * <p><b>Note:</b> This class is not thread-safe. 66c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * 67c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * @author Kevin Bourrillion 684fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev * @since 10.0 694fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev */ 704fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev@Beta 714fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev@GwtCompatible(emulated=true) 724fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataevpublic final class Stopwatch { 734fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev private final Ticker ticker; 744fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev private boolean isRunning; 754fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev private long elapsedNanos; 764fa7eab771ab8212e1058bd1a91061ff120c8fbbAlexey Bataev private long startTick; 77ef8225444452a1486bd721f3285301fe84643b00Stephen Hines 78ef8225444452a1486bd721f3285301fe84643b00Stephen Hines /** 79ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * Creates (but does not start) a new stopwatch using {@link System#nanoTime} 80ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * as its time source. 81ef8225444452a1486bd721f3285301fe84643b00Stephen Hines */ 82ef8225444452a1486bd721f3285301fe84643b00Stephen Hines public Stopwatch() { 83ef8225444452a1486bd721f3285301fe84643b00Stephen Hines this(Ticker.systemTicker()); 84ef8225444452a1486bd721f3285301fe84643b00Stephen Hines } 85ef8225444452a1486bd721f3285301fe84643b00Stephen Hines 86ef8225444452a1486bd721f3285301fe84643b00Stephen Hines /** 87ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * Creates (but does not start) a new stopwatch, using the specified time 88ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * source. 89ef8225444452a1486bd721f3285301fe84643b00Stephen Hines */ 90ef8225444452a1486bd721f3285301fe84643b00Stephen Hines public Stopwatch(Ticker ticker) { 91ef8225444452a1486bd721f3285301fe84643b00Stephen Hines this.ticker = checkNotNull(ticker); 92ef8225444452a1486bd721f3285301fe84643b00Stephen Hines } 93ef8225444452a1486bd721f3285301fe84643b00Stephen Hines 94ef8225444452a1486bd721f3285301fe84643b00Stephen Hines /** 95ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * Returns {@code true} if {@link #start()} has been called on this stopwatch, 96ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * and {@link #stop()} has not been called since the last call to {@code 97ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * start()}. 98ef8225444452a1486bd721f3285301fe84643b00Stephen Hines */ 99ef8225444452a1486bd721f3285301fe84643b00Stephen Hines public boolean isRunning() { 100ef8225444452a1486bd721f3285301fe84643b00Stephen Hines return isRunning; 101ef8225444452a1486bd721f3285301fe84643b00Stephen Hines } 102ef8225444452a1486bd721f3285301fe84643b00Stephen Hines 103ef8225444452a1486bd721f3285301fe84643b00Stephen Hines /** 104ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * Starts the stopwatch. 105ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * 106ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * @return this {@code Stopwatch} instance 107ef8225444452a1486bd721f3285301fe84643b00Stephen Hines * @throws IllegalStateException if the stopwatch is already running. 108ef8225444452a1486bd721f3285301fe84643b00Stephen Hines */ 109ef8225444452a1486bd721f3285301fe84643b00Stephen Hines public Stopwatch start() { 110ef8225444452a1486bd721f3285301fe84643b00Stephen Hines checkState(!isRunning); 111ef8225444452a1486bd721f3285301fe84643b00Stephen Hines isRunning = true; 112ef8225444452a1486bd721f3285301fe84643b00Stephen Hines startTick = ticker.read(); 113ef8225444452a1486bd721f3285301fe84643b00Stephen Hines return this; 114c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev } 115c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev 116c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev /** 117c640058aa7f224a71ce3b1d2601d84e1b57f82d3Alexey Bataev * Stops the stopwatch. Future reads will return the fixed duration that had 118 * elapsed up to this point. 119 * 120 * @return this {@code Stopwatch} instance 121 * @throws IllegalStateException if the stopwatch is already stopped. 122 */ 123 public Stopwatch stop() { 124 long tick = ticker.read(); 125 checkState(isRunning); 126 isRunning = false; 127 elapsedNanos += tick - startTick; 128 return this; 129 } 130 131 /** 132 * Sets the elapsed time for this stopwatch to zero, 133 * and places it in a stopped state. 134 * 135 * @return this {@code Stopwatch} instance 136 */ 137 public Stopwatch reset() { 138 elapsedNanos = 0; 139 isRunning = false; 140 return this; 141 } 142 143 private long elapsedNanos() { 144 return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos; 145 } 146 147 /** 148 * Returns the current elapsed time shown on this stopwatch, expressed 149 * in the desired time unit, with any fraction rounded down. 150 * 151 * <p>Note that the overhead of measurement can be more than a microsecond, so 152 * it is generally not useful to specify {@link TimeUnit#NANOSECONDS} 153 * precision here. 154 */ 155 public long elapsedTime(TimeUnit desiredUnit) { 156 return desiredUnit.convert(elapsedNanos(), NANOSECONDS); 157 } 158 159 /** 160 * Returns the current elapsed time shown on this stopwatch, expressed 161 * in milliseconds, with any fraction rounded down. This is identical to 162 * {@code elapsedTime(TimeUnit.MILLISECONDS}. 163 */ 164 public long elapsedMillis() { 165 return elapsedTime(MILLISECONDS); 166 } 167 168 private static TimeUnit chooseUnit(long nanos) { 169 if (SECONDS.convert(nanos, NANOSECONDS) > 0) { 170 return SECONDS; 171 } 172 if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { 173 return MILLISECONDS; 174 } 175 if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { 176 return MICROSECONDS; 177 } 178 return NANOSECONDS; 179 } 180 181 private static String abbreviate(TimeUnit unit) { 182 switch (unit) { 183 case NANOSECONDS: 184 return "ns"; 185 case MICROSECONDS: 186 return "\u03bcs"; // μs 187 case MILLISECONDS: 188 return "ms"; 189 case SECONDS: 190 return "s"; 191 default: 192 throw new AssertionError(); 193 } 194 } 195} 196 197