Skip to content

Commit 2359ee5

Browse files
committed
Refactored StopWatch and NanoWatch to deprecate ambiguous time unit methods and replace with unit-specific ones; fixed accumulation between stopwatch types
1 parent 7600a2b commit 2359ee5

File tree

4 files changed

+244
-37
lines changed

4 files changed

+244
-37
lines changed

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/util/NanoWatch.java

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@
3030
package com.oracle.labs.mlrg.olcut.util;
3131

3232
import java.io.Serializable;
33+
import java.text.DecimalFormat;
34+
import java.util.concurrent.TimeUnit;
3335

3436
/**
3537
* A nanosecond stop watch.
3638
*/
3739
public class NanoWatch extends StopWatch implements Serializable {
40+
public static final DecimalFormat nanoPartFormat = new DecimalFormat("#,###");
3841

3942
/**
4043
* Starts the timer.
@@ -54,27 +57,86 @@ public void stop() {
5457
time += lastTime;
5558
}
5659

60+
/**
61+
* Get the total completed time in this NanoWatch in nanoseconds
62+
*
63+
* @return the total time in nanos
64+
*/
5765
public long getTimeNanos() {
5866
return time;
5967
}
6068

69+
/**
70+
* Get the most recent completed time in nanoseconds
71+
*
72+
* @return the most recent time in nanos
73+
*/
6174
public long getLastTimeNanos() {
6275
return lastTime;
6376
}
6477

6578
/**
66-
* Gets the time for this nano stop watch in milliseconds.
67-
* @return the accumulated time for this stop watch in milliseconds.
79+
* Gets the time that this NanoWatch last started in nanoseconds since the Epoch
80+
*
81+
* @return the last start time in nanoseconds since the Epoch
82+
*/
83+
public long getLastStartNanos() {
84+
return lastStart;
85+
}
86+
87+
/**
88+
* Gets the average length of time per start/stop cycle of this NanoWatch in nanoseconds
89+
*
90+
* @return the average time per click of this NanoWatch in nanos
6891
*/
69-
public double getTimeMillis() {
70-
return getTime() / 1000000.0;
92+
public double getAvgTimeNanos() {
93+
return getTimeNanos() / (double)clicks;
7194
}
7295

73-
public double getLastTimeMillis() {
74-
return lastTime / 1000000.0;
96+
/**
97+
* Returns a string version of the total accumulated time of this StopWatch, scaled
98+
* appropriately depending on the amount of time. The full time to the nanosecond is
99+
* always shown, but the format varies depending on how much time is represented.
100+
* Times less than a second returns the number of nanoseconds. Times between
101+
* a second and a day return hh:mm:ss and nanos. Times greater than a day add a
102+
* number of days preceding the previous format.
103+
*
104+
* @return a string representing the time
105+
*/
106+
@Override
107+
public String toString() {
108+
return formatNanosecondTime(time);
109+
}
110+
111+
public static final String formatNanosecondTime(long nanos) {
112+
//
113+
// Are we less than a second?
114+
if (nanos < TimeUnit.SECONDS.toNanos(1)) {
115+
return String.format("%s nanos", nanoPartFormat.format(nanos));
116+
}
117+
118+
//
119+
// More than a second but less than a day?
120+
if (nanos < TimeUnit.DAYS.toNanos(1)) {
121+
return String.format("%02d:%02d:%02d and %s nanos",
122+
TimeUnit.NANOSECONDS.toHours(nanos),
123+
TimeUnit.NANOSECONDS.toMinutes(nanos) % TimeUnit.HOURS.toMinutes(1),
124+
TimeUnit.NANOSECONDS.toSeconds(nanos) % TimeUnit.MINUTES.toSeconds(1),
125+
nanoPartFormat.format(nanos % TimeUnit.SECONDS.toNanos(1)));
126+
}
127+
128+
//
129+
// More than a day
130+
return String.format("%d days %02d:%02d:%02d and %s nanos",
131+
TimeUnit.NANOSECONDS.toDays(nanos),
132+
TimeUnit.NANOSECONDS.toHours(nanos) % TimeUnit.DAYS.toHours(1),
133+
TimeUnit.NANOSECONDS.toMinutes(nanos) % TimeUnit.HOURS.toMinutes(1),
134+
TimeUnit.NANOSECONDS.toSeconds(nanos) % TimeUnit.MINUTES.toSeconds(1),
135+
nanoPartFormat.format(nanos % TimeUnit.SECONDS.toNanos(1)));
75136
}
76137

77-
public double getAvgTimeMillis() {
78-
return getTimeMillis() / (double) clicks;
138+
@Override
139+
public TimeUnit getUnit() {
140+
return TimeUnit.NANOSECONDS;
79141
}
80142
}

olcut-core/src/main/java/com/oracle/labs/mlrg/olcut/util/StopWatch.java

Lines changed: 98 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -89,24 +89,77 @@ public void reset() {
8989

9090
/**
9191
* Gets the number of milliseconds on the timer.
92+
* @deprecated Ambiguous units, use unit-specific get methods instead. Behavior of this method is undefined in subclasses.
9293
* @return The number of milliseconds on the timer.
9394
*/
9495
public long getTime() {
9596
return time;
9697
}
9798

99+
/**
100+
* Gets the total completed number of milliseconds counted in the StopWatch in milliseconds.
101+
*
102+
* @return the total counted time in milliseconds
103+
*/
104+
public long getTimeMillis() {
105+
return getUnit().toMillis(time);
106+
}
107+
108+
/**
109+
* Gets the last time recorded in milliseconds.
110+
* @deprecated Ambiguous units, use unit-specific get methods instead. Behavior of this method is undefined in subclasses.
111+
*
112+
* @return most recent start/stop time in milliseconds
113+
*/
98114
public long getLastTime() {
99115
return lastTime;
100116
}
101117

118+
/**
119+
* Gets the most recent complete time recorded in milliseconds.
120+
*
121+
* @return the most recent time in milliseconds
122+
*/
123+
public long getLastTimeMillis() {
124+
return getUnit().toMillis(lastTime);
125+
}
126+
127+
/**
128+
* Gets the last time the StopWatch was started
129+
* @deprecated Ambiguous units, use unit-specific get methods instead. Behavior of this method is undefined in subclasses.
130+
* @return the last start time
131+
*/
102132
public long getLastStart() {
103133
return lastStart;
104134
}
105135

136+
/**
137+
* Gets the last time the StopWatch was started in milliseconds since the Epoch
138+
*
139+
* @return the last start time in milliseconds
140+
*/
141+
public long getLastStartMillis() {
142+
return getUnit().toMillis(lastStart);
143+
}
144+
145+
/**
146+
* Gets the average time per click (start/stop)
147+
* @deprecated Ambiguous units, use unit-specific get methods instead. Behavior of this method is undefined in subclasses.
148+
* @return
149+
*/
106150
public double getAvgTime() {
107151
return getTime() / (double) clicks;
108152
}
109153

154+
/**
155+
* Gets the average length of time per click (start/stop cycle) in millliseconds
156+
*
157+
* @return the average time per click in milliseconds
158+
*/
159+
public double getAvgTimeMillis() {
160+
return getTimeMillis() / (double) clicks;
161+
}
162+
110163
/**
111164
* Gets the number of times that the watch was started and stopped since the last
112165
* reset.
@@ -121,7 +174,7 @@ public void accumulate(StopWatch sw) {
121174
if(sw == null) {
122175
return;
123176
}
124-
time += sw.time;
177+
time += getUnit().convert(sw.time, sw.getUnit());
125178
clicks += sw.clicks;
126179
}
127180

@@ -133,46 +186,60 @@ public void accumulate(StopWatch sw) {
133186
* a minute and a day return hh:MM:ss.mmm. Times greater than a day return a more
134187
* verbose string with each of days, hours, minute, seconds(.mmm) labeled.
135188
*
136-
* @return a string representing teh time
189+
* @return a string representing the time
137190
*/
191+
@Override
138192
public String toString() {
139-
if(time < 1000) {
140-
return String.format("0.%03ds", time);
141-
}
142-
143-
long secs = TimeUnit.MILLISECONDS.toSeconds(time);
193+
return formatMillisecondTime(time);
194+
}
144195

145-
if(secs < 60) {
146-
return String.format("%d.%03ds", secs,
147-
time % TimeUnit.SECONDS.toMillis(1));
196+
/**
197+
* Returns a human-readable version of the provided time in milliseconds, scaled
198+
* appropriately depending on the amount of time. The full time to the millisecond is
199+
* always shown, but the format varies depending on how much time is represented.
200+
* Times less than a minute return millisecond-precision seconds (s.mmm). Times between
201+
* a minute and a day return hh:MM:ss.mmm. Times greater than a day return a more
202+
* verbose string with each of days, hours, minute, seconds(.mmm) labeled.
203+
* @param millis the time to format in milliseconds
204+
*
205+
* @return a human-readable format of the time
206+
*/
207+
public static String formatMillisecondTime(long millis) {
208+
//
209+
// Less than a second?
210+
if(millis < 1000) {
211+
return String.format("0.%03ds", millis);
148212
}
149213

150-
long min = TimeUnit.MILLISECONDS.toMinutes(time);
214+
long secs = TimeUnit.MILLISECONDS.toSeconds(millis);
151215

152-
if(min < 60) {
153-
return String.format("00:%02d:%02d.%03d", min,
154-
TimeUnit.MILLISECONDS.toSeconds(time) % TimeUnit.MINUTES.toSeconds(1),
155-
time % TimeUnit.SECONDS.toMillis(1));
216+
//
217+
// Less than a minute?
218+
if(secs < 60) {
219+
return String.format("%d.%03ds",
220+
secs,
221+
millis % TimeUnit.SECONDS.toMillis(1));
156222
}
157223

158-
long h = TimeUnit.MILLISECONDS.toHours(time);
159-
224+
//
225+
// Less than a day?
226+
long h = TimeUnit.MILLISECONDS.toHours(millis);
160227
if (h < 24) {
161-
return String.format("%02d:%02d:%02d.%03d", h,
162-
TimeUnit.MILLISECONDS.toMinutes(time) % TimeUnit.HOURS.toMinutes(1),
163-
TimeUnit.MILLISECONDS.toSeconds(time) % TimeUnit.MINUTES.toSeconds(1),
164-
time % TimeUnit.SECONDS.toMillis(1));
228+
return String.format("%02d:%02d:%02d.%03d",
229+
h,
230+
TimeUnit.MILLISECONDS.toMinutes(millis) % TimeUnit.HOURS.toMinutes(1),
231+
TimeUnit.MILLISECONDS.toSeconds(millis) % TimeUnit.MINUTES.toSeconds(1),
232+
TimeUnit.MILLISECONDS.toMillis(millis) % TimeUnit.SECONDS.toMillis(1));
165233
}
166234

167-
long d = TimeUnit.MILLISECONDS.toDays(time);
235+
long d = TimeUnit.MILLISECONDS.toDays(millis);
168236

169237
return String.format("%d days %d hours %d mins %d.%03d seconds",
170238
d,
171-
TimeUnit.MILLISECONDS.toHours(time) % TimeUnit.DAYS.toHours(1),
172-
TimeUnit.MILLISECONDS.toMinutes(time) % TimeUnit.HOURS.toMinutes(1),
173-
TimeUnit.MILLISECONDS.toSeconds(time) % TimeUnit.MINUTES.toSeconds(1),
174-
time % TimeUnit.SECONDS.toMillis(1));
175-
}
239+
TimeUnit.MILLISECONDS.toHours(millis) % TimeUnit.DAYS.toHours(1),
240+
TimeUnit.MILLISECONDS.toMinutes(millis) % TimeUnit.HOURS.toMinutes(1),
241+
TimeUnit.MILLISECONDS.toSeconds(millis) % TimeUnit.MINUTES.toSeconds(1),
242+
TimeUnit.MILLISECONDS.toMillis(millis) % TimeUnit.SECONDS.toMillis(1)); }
176243

177244
/**
178245
* Creates a string representation to the nearest largest appropriate time unit.
@@ -202,4 +269,8 @@ public static String toTimeString(double millis) {
202269

203270
return String.format("%.2fh", h);
204271
}
272+
273+
public TimeUnit getUnit() {
274+
return TimeUnit.MILLISECONDS;
275+
}
205276
} // StopWatch
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.oracle.labs.mlrg.olcut.util;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.concurrent.TimeUnit;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class NanoWatchTest {
10+
public static class TestNanoWatch extends NanoWatch {
11+
public void setTime(long time) {
12+
this.time = time;
13+
}
14+
}
15+
16+
@Test
17+
public void toStringTest() {
18+
TestNanoWatch nw = new TestNanoWatch();
19+
nw.setTime(987654321);
20+
assertEquals("987,654,321 nanos", nw.toString());
21+
22+
nw.setTime(TimeUnit.MINUTES.toNanos(42) + 321);
23+
assertEquals("00:42:00 and 321 nanos", nw.toString());
24+
25+
nw.setTime(TimeUnit.DAYS.toNanos(5) + 654321);
26+
assertEquals("5 days 00:00:00 and 654,321 nanos", nw.toString());
27+
}
28+
29+
@Test
30+
public void conversionTest() {
31+
TestNanoWatch nw = new TestNanoWatch();
32+
//
33+
// Set 10 millis worth of nanos
34+
nw.setTime(TimeUnit.MILLISECONDS.toNanos(10));
35+
36+
assertEquals(10, nw.getTimeMillis());
37+
}
38+
39+
@Test
40+
public void accumulateTest() {
41+
StopWatchTest.TestStopWatch sw = new StopWatchTest.TestStopWatch();
42+
//
43+
// Set 5 seconds on the milli timer
44+
sw.setTime(TimeUnit.SECONDS.toMillis(5));
45+
46+
//
47+
// Add it to a nano time
48+
TestNanoWatch nw = new TestNanoWatch();
49+
nw.accumulate(sw);
50+
51+
//
52+
// We should have 5 seconds on the nanowatch, not anything else
53+
assertEquals(5, TimeUnit.NANOSECONDS.toSeconds(nw.getTimeNanos()));
54+
}
55+
}

olcut-core/src/test/java/com/oracle/labs/mlrg/olcut/util/StopWatchTest.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
import org.junit.jupiter.api.Test;
44

5+
import java.util.concurrent.TimeUnit;
6+
57
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
69

710
public class StopWatchTest {
8-
public class TestStopWatch extends StopWatch {
11+
public static class TestStopWatch extends StopWatch {
912
public void setTime(long time) {
1013
this.time = time;
1114
}
1215
}
1316

1417
@Test
15-
public void ToStringTest() {
18+
public void toStringTest() {
1619
TestStopWatch sw = new TestStopWatch();
1720
//
1821
// Try a time in milliseconds
@@ -35,4 +38,20 @@ public void ToStringTest() {
3538
sw.setTime((12 * 24 * 60 * 60 * 1000) + (7 * 60 * 60 * 1000) + (37 * 60 * 1000) + (1 * 1000) + 42);
3639
assertEquals("12 days 7 hours 37 mins 1.042 seconds", sw.toString());
3740
}
41+
42+
@Test
43+
public void accumulateNanosTest() throws Exception {
44+
TestStopWatch sw = new TestStopWatch();
45+
sw.setTime(5000);
46+
47+
NanoWatchTest.TestNanoWatch nw = new NanoWatchTest.TestNanoWatch();
48+
nw.setTime(100); // 10 nanoseconds
49+
50+
sw.accumulate(nw);
51+
assertEquals(5000, sw.getTimeMillis());
52+
53+
nw.setTime(TimeUnit.MILLISECONDS.toNanos(1));
54+
sw.accumulate(nw);
55+
assertEquals(5001, sw.getTimeMillis());
56+
}
3857
}

0 commit comments

Comments
 (0)