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    }