1/* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.base; 18 19import static com.google.common.base.Preconditions.checkNotNull; 20import static com.google.common.base.Preconditions.checkState; 21import static java.util.concurrent.TimeUnit.DAYS; 22import static java.util.concurrent.TimeUnit.HOURS; 23import static java.util.concurrent.TimeUnit.MICROSECONDS; 24import static java.util.concurrent.TimeUnit.MILLISECONDS; 25import static java.util.concurrent.TimeUnit.MINUTES; 26import static java.util.concurrent.TimeUnit.NANOSECONDS; 27import static java.util.concurrent.TimeUnit.SECONDS; 28 29import com.google.common.annotations.Beta; 30import com.google.common.annotations.GwtCompatible; 31 32import java.util.concurrent.TimeUnit; 33 34/** 35 * An object that measures elapsed time in nanoseconds. It is useful to measure 36 * elapsed time using this class instead of direct calls to {@link 37 * System#nanoTime} for a few reasons: 38 * 39 * <ul> 40 * <li>An alternate time source can be substituted, for testing or performance 41 * reasons. 42 * <li>As documented by {@code nanoTime}, the value returned has no absolute 43 * meaning, and can only be interpreted as relative to another timestamp 44 * returned by {@code nanoTime} at a different time. {@code Stopwatch} is a 45 * more effective abstraction because it exposes only these relative values, 46 * not the absolute ones. 47 * </ul> 48 * 49 * <p>Basic usage: 50 * <pre> 51 * Stopwatch stopwatch = Stopwatch.{@link #createStarted createStarted}(); 52 * doSomething(); 53 * stopwatch.{@link #stop stop}(); // optional 54 * 55 * long millis = stopwatch.elapsed(MILLISECONDS); 56 * 57 * log.info("time: " + stopwatch); // formatted string like "12.3 ms"</pre> 58 * 59 * <p>Stopwatch methods are not idempotent; it is an error to start or stop a 60 * stopwatch that is already in the desired state. 61 * 62 * <p>When testing code that uses this class, use 63 * {@link #createUnstarted(Ticker)} or {@link #createStarted(Ticker)} to 64 * supply a fake or mock ticker. 65 * <!-- TODO(kevinb): restore the "such as" --> This allows you to 66 * simulate any valid behavior of the stopwatch. 67 * 68 * <p><b>Note:</b> This class is not thread-safe. 69 * 70 * @author Kevin Bourrillion 71 * @since 10.0 72 */ 73@Beta 74@GwtCompatible(emulated = true) 75public final class Stopwatch { 76 private final Ticker ticker; 77 private boolean isRunning; 78 private long elapsedNanos; 79 private long startTick; 80 81 /** 82 * Creates (but does not start) a new stopwatch using {@link System#nanoTime} 83 * as its time source. 84 * 85 * @since 15.0 86 */ 87 public static Stopwatch createUnstarted() { 88 return new Stopwatch(); 89 } 90 91 /** 92 * Creates (but does not start) a new stopwatch, using the specified time 93 * source. 94 * 95 * @since 15.0 96 */ 97 public static Stopwatch createUnstarted(Ticker ticker) { 98 return new Stopwatch(ticker); 99 } 100 101 /** 102 * Creates (and starts) a new stopwatch using {@link System#nanoTime} 103 * as its time source. 104 * 105 * @since 15.0 106 */ 107 public static Stopwatch createStarted() { 108 return new Stopwatch().start(); 109 } 110 111 /** 112 * Creates (and starts) a new stopwatch, using the specified time 113 * source. 114 * 115 * @since 15.0 116 */ 117 public static Stopwatch createStarted(Ticker ticker) { 118 return new Stopwatch(ticker).start(); 119 } 120 121 /** 122 * Creates (but does not start) a new stopwatch using {@link System#nanoTime} 123 * as its time source. 124 * 125 * @deprecated Use {@link Stopwatch#createUnstarted()} instead. This 126 * constructor is scheduled to be removed in Guava release 17.0. 127 */ 128 @Deprecated 129 public Stopwatch() { 130 this(Ticker.systemTicker()); 131 } 132 133 /** 134 * Creates (but does not start) a new stopwatch, using the specified time 135 * source. 136 * 137 * @deprecated Use {@link Stopwatch#createUnstarted(Ticker)} instead. This 138 * constructor is scheduled to be removed in Guava release 17.0. 139 */ 140 @Deprecated 141 public Stopwatch(Ticker ticker) { 142 this.ticker = checkNotNull(ticker, "ticker"); 143 } 144 145 /** 146 * Returns {@code true} if {@link #start()} has been called on this stopwatch, 147 * and {@link #stop()} has not been called since the last call to {@code 148 * start()}. 149 */ 150 public boolean isRunning() { 151 return isRunning; 152 } 153 154 /** 155 * Starts the stopwatch. 156 * 157 * @return this {@code Stopwatch} instance 158 * @throws IllegalStateException if the stopwatch is already running. 159 */ 160 public Stopwatch start() { 161 checkState(!isRunning, "This stopwatch is already running."); 162 isRunning = true; 163 startTick = ticker.read(); 164 return this; 165 } 166 167 /** 168 * Stops the stopwatch. Future reads will return the fixed duration that had 169 * elapsed up to this point. 170 * 171 * @return this {@code Stopwatch} instance 172 * @throws IllegalStateException if the stopwatch is already stopped. 173 */ 174 public Stopwatch stop() { 175 long tick = ticker.read(); 176 checkState(isRunning, "This stopwatch is already stopped."); 177 isRunning = false; 178 elapsedNanos += tick - startTick; 179 return this; 180 } 181 182 /** 183 * Sets the elapsed time for this stopwatch to zero, 184 * and places it in a stopped state. 185 * 186 * @return this {@code Stopwatch} instance 187 */ 188 public Stopwatch reset() { 189 elapsedNanos = 0; 190 isRunning = false; 191 return this; 192 } 193 194 private long elapsedNanos() { 195 return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos; 196 } 197 198 /** 199 * Returns the current elapsed time shown on this stopwatch, expressed 200 * in the desired time unit, with any fraction rounded down. 201 * 202 * <p>Note that the overhead of measurement can be more than a microsecond, so 203 * it is generally not useful to specify {@link TimeUnit#NANOSECONDS} 204 * precision here. 205 * 206 * @since 14.0 (since 10.0 as {@code elapsedTime()}) 207 */ 208 public long elapsed(TimeUnit desiredUnit) { 209 return desiredUnit.convert(elapsedNanos(), NANOSECONDS); 210 } 211 212 private static TimeUnit chooseUnit(long nanos) { 213 if (DAYS.convert(nanos, NANOSECONDS) > 0) { 214 return DAYS; 215 } 216 if (HOURS.convert(nanos, NANOSECONDS) > 0) { 217 return HOURS; 218 } 219 if (MINUTES.convert(nanos, NANOSECONDS) > 0) { 220 return MINUTES; 221 } 222 if (SECONDS.convert(nanos, NANOSECONDS) > 0) { 223 return SECONDS; 224 } 225 if (MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { 226 return MILLISECONDS; 227 } 228 if (MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { 229 return MICROSECONDS; 230 } 231 return NANOSECONDS; 232 } 233 234 private static String abbreviate(TimeUnit unit) { 235 switch (unit) { 236 case NANOSECONDS: 237 return "ns"; 238 case MICROSECONDS: 239 return "\u03bcs"; // μs 240 case MILLISECONDS: 241 return "ms"; 242 case SECONDS: 243 return "s"; 244 case MINUTES: 245 return "min"; 246 case HOURS: 247 return "h"; 248 case DAYS: 249 return "d"; 250 default: 251 throw new AssertionError(); 252 } 253 } 254} 255 256