|
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 } |