001 package hirondelle.web4j.util; 002 003 import java.math.BigDecimal; 004 005 /** 006 Allows timing of the execution of any block of code. 007 008 Example use case: 009 <PRE> 010 Stopwatch stopwatch = new Stopwatch(); 011 stopwatch.start(); 012 //..perform operations 013 stopwatch.stop(); 014 015 //timed in nanos, but toString is expressed in millis, 016 //to three decimal places, to reflect the real resolution of most systems: 017 System.out.println("The reading on the stopwatch is: " + stopwatch); 018 019 //reuse the same stopwatch again 020 //Note that there is no need to call a reset method. 021 stopwatch.start(); 022 //..perform operations 023 stopwatch.stop(); 024 025 //perform a numeric comparison, using raw nanoseconds: 026 if ( stopwatch.toValue() > 5 ) { 027 System.out.println("The reading is high: " + stopwatch); 028 } 029 </PRE> 030 031 <P>The value on the stopwatch may be inspected at any time using the 032 {@link #toString} (millis) and {@link #toValue} (nanos) methods. 033 034 <P><b>Example 2</b> 035 <br>To time the various steps in a long startup or initialization task, your code may take the form: 036 <PRE> 037 Stopwatch stopwatch = new Stopwatch(); 038 stopwatch.start(); 039 //..perform operation 1 040 log("Step 1 completed " + stopwatch + " after start."); 041 042 //perform operation 2 043 log("Step 2 completed " + stopwatch + " after start."); 044 045 //perform the last operation, operation 3 046 stopwatch.stop(); 047 log("Final Step 3 completed " + stopwatch + " after start") ; 048 </PRE> 049 050 <P><i>Implementation Note:</i></br> 051 This class uses {@link System#nanoTime()}, not {@link System#currentTimeMillis()}, to 052 perform its timing operations. 053 */ 054 public final class Stopwatch { 055 056 /** 057 Start the stopwatch. 058 059 <P>You cannot call this method if the stopwatch is already started. 060 */ 061 public void start(){ 062 if (fIsRunning) { 063 throw new IllegalStateException("Must stop before calling start again."); 064 } 065 //reset both start and stop 066 fStart = getCurrentTime(); 067 fStop = 0; 068 fIsRunning = true; 069 } 070 071 /** 072 Stop the stopwatch. 073 074 <P>You can only call this method if the stopwatch has been started. 075 */ 076 public void stop() { 077 if (!fIsRunning) { 078 throw new IllegalStateException("Cannot stop if not currently running."); 079 } 080 fStop = getCurrentTime(); 081 fIsRunning = false; 082 } 083 084 /** 085 Return the current "reading" on the stopwatch, in milliseconds, in a format suitable 086 typical use cases. 087 088 <P>Example return value : '<tt>108.236 ms</tt>'. The underlying timing is in nanos, 089 but it's expressed here in millis, for two reasons: the resolution on most systems is 090 in the microsecond range, and the full 9 digits are less easy to reader. If you need 091 the raw nanos, just use {@link #toValue()} instead. 092 093 <P>Ref: https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks 094 */ 095 @Override public String toString() { 096 StringBuilder result = new StringBuilder(); 097 BigDecimal value = new BigDecimal(toValue());//scale is zero 098 //millis, with 3 decimals: 099 value = value.divide(MILLION, 3, BigDecimal.ROUND_HALF_EVEN); 100 result.append(value); 101 result.append(" ms"); 102 return result.toString(); 103 } 104 105 /** 106 Return the current "reading" on the stopwatch in raw nanoseconds, as a numeric type. 107 */ 108 public long toValue() { 109 long result = fStop == 0 ? (getCurrentTime() - fStart) : (fStop -fStart); 110 return result; 111 } 112 113 // PRIVATE 114 private long fStart; 115 private long fStop; 116 private boolean fIsRunning; 117 118 /** Converts from nanos to millis. */ 119 private static final BigDecimal MILLION = new BigDecimal("1000000"); 120 121 private long getCurrentTime(){ 122 return System.nanoTime(); 123 } 124 }