classes/hirondelle/web4j/util/Stopwatch.java
changeset 0 3060119b1292
equal deleted inserted replaced
-1:000000000000 0:3060119b1292
       
     1 package hirondelle.web4j.util;
       
     2 
       
     3 import java.math.BigDecimal;
       
     4 
       
     5 /**
       
     6  Allows timing of the execution of any block of code.
       
     7 
       
     8  Example use case:
       
     9 <PRE>
       
    10 Stopwatch stopwatch = new Stopwatch();
       
    11 stopwatch.start();
       
    12 //..perform operations
       
    13 stopwatch.stop();
       
    14 
       
    15 //timed in nanos, but toString is expressed in millis,
       
    16 //to three decimal places, to reflect the real resolution of most systems:
       
    17 System.out.println("The reading on the stopwatch is: " + stopwatch);
       
    18 
       
    19 //reuse the same stopwatch again
       
    20 //Note that there is no need to call a reset method.
       
    21 stopwatch.start();
       
    22 //..perform operations
       
    23 stopwatch.stop();
       
    24 
       
    25 //perform a numeric comparison, using raw nanoseconds:
       
    26 if ( stopwatch.toValue() > 5 ) {
       
    27   System.out.println("The reading is high: " + stopwatch);
       
    28 }
       
    29 </PRE>
       
    30 
       
    31  <P>The value on the stopwatch may be inspected at any time using the  
       
    32  {@link #toString} (millis) and {@link #toValue} (nanos) methods.
       
    33  
       
    34  <P><b>Example 2</b>
       
    35  <br>To time the various steps in a long startup or initialization task, your code may take the form:
       
    36 <PRE>
       
    37 Stopwatch stopwatch = new Stopwatch();
       
    38 stopwatch.start();
       
    39 //..perform operation 1
       
    40 log("Step 1 completed " + stopwatch + " after start.");
       
    41 
       
    42 //perform operation 2
       
    43 log("Step 2 completed " + stopwatch + " after start.");
       
    44 
       
    45 //perform the last operation, operation 3
       
    46 stopwatch.stop();
       
    47 log("Final Step 3 completed " + stopwatch + " after start") ;
       
    48 </PRE>
       
    49 
       
    50 <P><i>Implementation Note:</i></br>
       
    51  This class uses {@link System#nanoTime()}, not {@link System#currentTimeMillis()}, to 
       
    52  perform its timing operations.
       
    53 */
       
    54 public final class Stopwatch {
       
    55 
       
    56   /**
       
    57    Start the stopwatch.
       
    58   
       
    59    <P>You cannot call this method if the stopwatch is already started.
       
    60   */
       
    61   public void start(){
       
    62     if (fIsRunning) {
       
    63       throw new IllegalStateException("Must stop before calling start again.");
       
    64     }
       
    65     //reset both start and stop
       
    66     fStart = getCurrentTime();
       
    67     fStop = 0;
       
    68     fIsRunning = true;
       
    69   }
       
    70 
       
    71   /**
       
    72    Stop the stopwatch.
       
    73   
       
    74    <P>You can only call this method if the stopwatch has been started.
       
    75   */
       
    76   public void stop() {
       
    77     if (!fIsRunning) {
       
    78       throw new IllegalStateException("Cannot stop if not currently running.");
       
    79     }
       
    80     fStop = getCurrentTime();
       
    81     fIsRunning = false;
       
    82   }
       
    83 
       
    84   /**
       
    85    Return the current "reading" on the stopwatch, in milliseconds, in a format suitable 
       
    86    typical use cases.
       
    87    
       
    88    <P>Example return value : '<tt>108.236 ms</tt>'. The underlying timing is in nanos, 
       
    89    but it's expressed here in millis, for two reasons: the resolution on most systems is 
       
    90    in the microsecond range, and the full 9 digits are less easy to reader. If you need 
       
    91    the raw nanos, just use {@link #toValue()} instead.
       
    92    
       
    93    <P>Ref: https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks
       
    94   */
       
    95   @Override public String toString() {
       
    96     StringBuilder result = new StringBuilder();
       
    97     BigDecimal value = new BigDecimal(toValue());//scale is zero
       
    98     //millis, with 3 decimals:
       
    99     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 }