blob: 5c02664a38acc83ee094abfd67be646804d52be3 [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.util;
Brian O'Connor8fdfb062014-01-08 17:47:57 -08002
Brian O'Connor2daf7a92014-01-14 11:26:35 -08003import java.util.Map.Entry;
Brian O'Connora5e5f832014-01-15 16:58:14 -08004import java.util.Queue;
Brian O'Connor8fdfb062014-01-08 17:47:57 -08005import java.util.concurrent.ConcurrentHashMap;
Brian O'Connora5e5f832014-01-15 16:58:14 -08006import java.util.concurrent.ConcurrentLinkedQueue;
Brian O'Connor8fdfb062014-01-08 17:47:57 -08007
8import org.slf4j.Logger;
9import org.slf4j.LoggerFactory;
10
11/**
12 * Class for collecting performance measurements
13 */
14public class PerformanceMonitor {
Ray Milkeyec838942014-04-09 11:28:43 -070015 private static final Logger log = LoggerFactory.getLogger(PerformanceMonitor.class);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080016
17 // experiment name -> PerformanceMonitor
Ray Milkey5c9f2db2014-04-09 10:31:21 -070018 private static final ConcurrentHashMap<String, PerformanceMonitor> PERF_MONS = new ConcurrentHashMap<>();
Ray Milkey269ffb92014-04-03 14:43:30 -070019
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080020 public static PerformanceMonitor experiment(String name) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -070021 PerformanceMonitor pm = PERF_MONS.get(name);
Ray Milkey269ffb92014-04-03 14:43:30 -070022 if (pm == null) {
23 pm = new PerformanceMonitor();
Ray Milkey5c9f2db2014-04-09 10:31:21 -070024 PerformanceMonitor existing = PERF_MONS.putIfAbsent(name, pm);
Ray Milkey269ffb92014-04-03 14:43:30 -070025 if (existing != null) {
26 pm = existing;
27 }
28 }
29 return pm;
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080030 }
31
32 // tag -> Measurements
33 private final ConcurrentHashMap<String, Queue<Measurement>> map = new ConcurrentHashMap<>();
34 private long overhead;
35 private long experimentStart = Long.MAX_VALUE;
Ray Milkeyec838942014-04-09 11:28:43 -070036 private static final double NORMALIZATION = Math.pow(10, 6);
Brian O'Connor2daf7a92014-01-14 11:26:35 -080037
Brian O'Connor8fdfb062014-01-08 17:47:57 -080038 /**
39 * Start a performance measurement, identified by a tag
Ray Milkey269ffb92014-04-03 14:43:30 -070040 * <p/>
Brian O'Connor8fdfb062014-01-08 17:47:57 -080041 * Note: Only a single measurement can use the same tag at a time.
Brian O'Connor2daf7a92014-01-14 11:26:35 -080042 * ..... not true anymore.
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080043 *
Brian O'Connor8fdfb062014-01-08 17:47:57 -080044 * @param tag for performance measurement
45 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080046 public Measurement startStep(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -070047 long start = System.nanoTime();
48 if (start < experimentStart) {
49 experimentStart = start;
50 }
51 Queue<Measurement> list = map.get(tag);
52 if (list == null) {
53 list = new ConcurrentLinkedQueue<Measurement>();
Ray Milkey2476cac2014-04-08 11:03:21 -070054 Queue<Measurement> existingList = map.putIfAbsent(tag, list);
55 if (existingList != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -070056 // someone concurrently added, using theirs
Ray Milkey2476cac2014-04-08 11:03:21 -070057 list = existingList;
Ray Milkey269ffb92014-04-03 14:43:30 -070058 }
59 }
60 Measurement m = new Measurement();
61 list.add(m);
62 m.start();
63 overhead += System.nanoTime() - start;
64 return m;
Brian O'Connor8fdfb062014-01-08 17:47:57 -080065 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080066
Brian O'Connor8fdfb062014-01-08 17:47:57 -080067 /**
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080068 * Stop a performance measurement.
Ray Milkey269ffb92014-04-03 14:43:30 -070069 * <p/>
Brian O'Connor8fdfb062014-01-08 17:47:57 -080070 * You must have already started a measurement with tag.
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080071 *
Brian O'Connor8fdfb062014-01-08 17:47:57 -080072 * @param tag for performance measurement
73 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080074 public void stopStep(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -070075 long time = System.nanoTime();
76 Queue<Measurement> list = map.get(tag);
77 if (list == null || list.size() == 0) {
78 log.error("Tag {} does not exist", tag);
79 }
80 list.peek().stop(time);
81 if (list.size() > 1) {
82 log.error("Tag {} has multiple measurements", tag);
83 }
84 overhead += System.nanoTime() - time;
Brian O'Connor8fdfb062014-01-08 17:47:57 -080085 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080086
Brian O'Connor8fdfb062014-01-08 17:47:57 -080087 /**
88 * Clear all performance measurements.
89 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080090 public void reset() {
Ray Milkey269ffb92014-04-03 14:43:30 -070091 map.clear();
92 overhead = 0;
93 experimentStart = Long.MAX_VALUE;
Brian O'Connor8fdfb062014-01-08 17:47:57 -080094 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080095
Brian O'Connor8fdfb062014-01-08 17:47:57 -080096 /**
97 * Write all performance measurements to the log
98 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080099 public void reportAll() {
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700100 StringBuilder result = new StringBuilder();
101
102 result.append("Performance Results: (avg/start/stop/count)\n");
Ray Milkey269ffb92014-04-03 14:43:30 -0700103 if (map.size() == 0) {
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700104 result.append("No Measurements");
105 log.error(result.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 return;
107 }
108 long experimentEnd = -1;
109 for (Entry<String, Queue<Measurement>> e : map.entrySet()) {
110 String key = e.getKey();
111 Queue<Measurement> list = e.getValue();
112 long total = 0, count = 0;
113 long start = Long.MAX_VALUE, stop = -1;
114 for (Measurement m : list) {
115 if (m.stop < 0) {
116 continue; // measurement has not been stopped
117 }
118 // Collect overall start and end times
119 if (m.start < start) {
120 start = m.start;
121 }
122 if (m.stop > stop) {
123 stop = m.stop;
124 if (stop > experimentEnd) {
125 experimentEnd = stop;
126 }
127 }
128 // Collect statistics for average
129 total += m.elapsed();
130 count++;
131 }
132 double avg = (double) total / count;
133 // Normalize start/stop
134 start -= experimentStart;
135 stop -= experimentStart;
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700136 result.append(key + '=' +
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700137 (avg / NORMALIZATION) + '/' +
138 (start / NORMALIZATION) + '/' +
139 (stop / NORMALIZATION) + '/' +
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700140 count + '\n');
Ray Milkey269ffb92014-04-03 14:43:30 -0700141 }
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700142 double overheadMs = overhead / NORMALIZATION;
143 double experimentElapsed = (experimentEnd - experimentStart) / NORMALIZATION;
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700144 result.append("TotalTime:" + experimentElapsed + "/Overhead:" + overheadMs);
145 log.error(result.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700146// log.error("Performance Results: {} with measurement overhead: {} ms", map, overheadMilli);
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800147 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800148
149 /**
150 * Write the performance measurement for a tag to the log
151 *
152 * @param tag the tag name.
153 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800154 public void reportStep(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 Queue<Measurement> list = map.get(tag);
156 if (list == null) {
157 return; //TODO
158 }
159 //TODO: fix this;
160 Measurement m = list.peek();
161 if (m != null) {
162 log.error("Performance Result: tag = {} start = {} stop = {} elapsed = {}",
163 tag, m.start, m.stop, m.toString());
164 } else {
165 log.error("Performance Result: unknown tag {}", tag);
166 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800167 }
168
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800169 /**
170 * A single performance measurement
171 */
Brian O'Connor2daf7a92014-01-14 11:26:35 -0800172 public static class Measurement {
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 long start;
174 long stop = -1;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800175
Ray Milkey269ffb92014-04-03 14:43:30 -0700176 /**
177 * Start the measurement
178 */
179 public void start() {
180 if (start <= 0) {
181 start = System.nanoTime();
182 }
183 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800184
Ray Milkey269ffb92014-04-03 14:43:30 -0700185 /**
186 * Stop the measurement
187 */
188 public void stop() {
189 long now = System.nanoTime();
190 stop(now);
191 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800192
Ray Milkey269ffb92014-04-03 14:43:30 -0700193 /**
194 * Stop the measurement at a specific time
195 *
196 * @param time to stop
197 */
198 public void stop(long time) {
199 if (stop <= 0) {
200 stop = time;
201 }
202 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800203
Ray Milkey269ffb92014-04-03 14:43:30 -0700204 /**
205 * Compute the elapsed time of the measurement in nanoseconds
206 *
207 * @return the measurement time in nanoseconds, or -1 if the measurement is stil running.
208 */
209 public long elapsed() {
210 if (stop <= 0) {
211 return -1;
212 } else {
213 return stop - start;
214 }
215 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800216
Ray Milkey269ffb92014-04-03 14:43:30 -0700217 /**
218 * Returns the number of milliseconds for the measurement as a String.
219 */
220 @Override
221 public String toString() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700222 double milli = elapsed() / NORMALIZATION;
223 double startMs = start / NORMALIZATION;
224 double stopMs = stop / NORMALIZATION;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800225
Ray Milkey269ffb92014-04-03 14:43:30 -0700226 return milli + "ms/" + startMs + '/' + stopMs;
227 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800228 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800229
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800230 @Deprecated
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700231 private static final PerformanceMonitor PERFORMANCE_MONITOR_INSTANCE = new PerformanceMonitor();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800232
233 @Deprecated
234 public static Measurement start(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700235 return PERFORMANCE_MONITOR_INSTANCE.startStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800236 }
237
238 @Deprecated
239 public static void stop(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700240 PERFORMANCE_MONITOR_INSTANCE.stopStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800241 }
242
243 @Deprecated
244 public static void clear() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700245 PERFORMANCE_MONITOR_INSTANCE.reset();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800246 }
247
248 @Deprecated
249 public static void report() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700250 PERFORMANCE_MONITOR_INSTANCE.reportAll();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800251 }
252
253 @Deprecated
254 public static void report(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700255 PERFORMANCE_MONITOR_INSTANCE.reportStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800256 }
257
Ray Milkey45614c52014-04-07 16:30:54 -0700258 public static void main(String[] args) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 // test the measurement overhead
260 String tag;
261 for (int i = 0; i < 2; i++) {
262 tag = "foo foo foo";
263 Measurement m;
264 m = start(tag);
265 System.out.println(tag);
266 m.stop();
267 m = start(tag);
268 System.out.println(tag);
269 m.stop();
270 m = start(tag);
271 System.out.println(tag);
272 m.stop();
273 m = start(tag);
274 System.out.println(tag);
275 m.stop();
276 tag = "bar";
277 start(tag);
278 stop(tag);
279 tag = "baz";
280 start(tag);
281 stop(tag);
282 report();
283 clear();
284 }
285 for (int i = 0; i < 100; i++) {
286 tag = "a";
287 start(tag);
288 stop(tag);
289 start(tag);
290 stop(tag);
brian6df88232014-01-14 17:05:07 -0800291
Ray Milkey269ffb92014-04-03 14:43:30 -0700292 start(tag);
293 stop(tag);
294 start(tag);
295 stop(tag);
296 start(tag);
297 stop(tag);
298 start(tag);
299 stop(tag);
300 start(tag);
301 stop(tag);
302 start(tag);
303 stop(tag);
brian6df88232014-01-14 17:05:07 -0800304
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 tag = "b";
306 start(tag);
307 stop(tag);
308 tag = "c";
309 start(tag);
310 stop(tag);
311 report();
312 clear();
313 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800314 }
315}