blob: 389453f8778b7dbb520611849d05344c33c28da4 [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/**
Ray Milkeyb41100a2014-04-10 10:42:15 -070012 * Class for collecting performance measurements.
Brian O'Connor8fdfb062014-01-08 17:47:57 -080013 */
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 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -070039 * 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);
Pavlin Radoslavov9a496ad2014-04-09 16:51:25 -070079 overhead += System.nanoTime() - time;
80 return;
Ray Milkey269ffb92014-04-03 14:43:30 -070081 }
82 list.peek().stop(time);
83 if (list.size() > 1) {
84 log.error("Tag {} has multiple measurements", tag);
85 }
86 overhead += System.nanoTime() - time;
Brian O'Connor8fdfb062014-01-08 17:47:57 -080087 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080088
Brian O'Connor8fdfb062014-01-08 17:47:57 -080089 /**
90 * Clear all performance measurements.
91 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080092 public void reset() {
Ray Milkey269ffb92014-04-03 14:43:30 -070093 map.clear();
94 overhead = 0;
95 experimentStart = Long.MAX_VALUE;
Brian O'Connor8fdfb062014-01-08 17:47:57 -080096 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -080097
Brian O'Connor8fdfb062014-01-08 17:47:57 -080098 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -070099 * Write all performance measurements to the log.
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800100 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800101 public void reportAll() {
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700102 StringBuilder result = new StringBuilder();
103
104 result.append("Performance Results: (avg/start/stop/count)\n");
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 if (map.size() == 0) {
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700106 result.append("No Measurements");
107 log.error(result.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700108 return;
109 }
110 long experimentEnd = -1;
111 for (Entry<String, Queue<Measurement>> e : map.entrySet()) {
112 String key = e.getKey();
113 Queue<Measurement> list = e.getValue();
114 long total = 0, count = 0;
115 long start = Long.MAX_VALUE, stop = -1;
116 for (Measurement m : list) {
117 if (m.stop < 0) {
118 continue; // measurement has not been stopped
119 }
120 // Collect overall start and end times
121 if (m.start < start) {
122 start = m.start;
123 }
124 if (m.stop > stop) {
125 stop = m.stop;
126 if (stop > experimentEnd) {
127 experimentEnd = stop;
128 }
129 }
130 // Collect statistics for average
131 total += m.elapsed();
132 count++;
133 }
134 double avg = (double) total / count;
135 // Normalize start/stop
136 start -= experimentStart;
137 stop -= experimentStart;
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700138 result.append(key + '=' +
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700139 (avg / NORMALIZATION) + '/' +
140 (start / NORMALIZATION) + '/' +
141 (stop / NORMALIZATION) + '/' +
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700142 count + '\n');
Ray Milkey269ffb92014-04-03 14:43:30 -0700143 }
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700144 double overheadMs = overhead / NORMALIZATION;
145 double experimentElapsed = (experimentEnd - experimentStart) / NORMALIZATION;
Pavlin Radoslavov424150c2014-04-09 12:12:36 -0700146 result.append("TotalTime:" + experimentElapsed + "/Overhead:" + overheadMs);
147 log.error(result.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700148// log.error("Performance Results: {} with measurement overhead: {} ms", map, overheadMilli);
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800149 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800150
151 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700152 * Write the performance measurement for a tag to the log.
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800153 *
154 * @param tag the tag name.
155 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800156 public void reportStep(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700157 Queue<Measurement> list = map.get(tag);
158 if (list == null) {
159 return; //TODO
160 }
161 //TODO: fix this;
162 Measurement m = list.peek();
163 if (m != null) {
164 log.error("Performance Result: tag = {} start = {} stop = {} elapsed = {}",
165 tag, m.start, m.stop, m.toString());
166 } else {
167 log.error("Performance Result: unknown tag {}", tag);
168 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800169 }
170
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800171 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700172 * A single performance measurement.
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800173 */
Brian O'Connor2daf7a92014-01-14 11:26:35 -0800174 public static class Measurement {
Ray Milkey269ffb92014-04-03 14:43:30 -0700175 long start;
176 long stop = -1;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800177
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700179 * Start the measurement.
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 */
181 public void start() {
182 if (start <= 0) {
183 start = System.nanoTime();
184 }
185 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800186
Ray Milkey269ffb92014-04-03 14:43:30 -0700187 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700188 * Stop the measurement.
Ray Milkey269ffb92014-04-03 14:43:30 -0700189 */
190 public void stop() {
191 long now = System.nanoTime();
192 stop(now);
193 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800194
Ray Milkey269ffb92014-04-03 14:43:30 -0700195 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700196 * Stop the measurement at a specific time.
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 *
198 * @param time to stop
199 */
200 public void stop(long time) {
201 if (stop <= 0) {
202 stop = time;
203 }
204 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800205
Ray Milkey269ffb92014-04-03 14:43:30 -0700206 /**
Ray Milkeyb41100a2014-04-10 10:42:15 -0700207 * Compute the elapsed time of the measurement in nanoseconds.
Ray Milkey269ffb92014-04-03 14:43:30 -0700208 *
209 * @return the measurement time in nanoseconds, or -1 if the measurement is stil running.
210 */
211 public long elapsed() {
212 if (stop <= 0) {
213 return -1;
214 } else {
215 return stop - start;
216 }
217 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800218
Ray Milkey269ffb92014-04-03 14:43:30 -0700219 /**
220 * Returns the number of milliseconds for the measurement as a String.
221 */
222 @Override
223 public String toString() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700224 double milli = elapsed() / NORMALIZATION;
225 double startMs = start / NORMALIZATION;
226 double stopMs = stop / NORMALIZATION;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800227
Ray Milkey269ffb92014-04-03 14:43:30 -0700228 return milli + "ms/" + startMs + '/' + stopMs;
229 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800230 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800231
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800232 @Deprecated
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700233 private static final PerformanceMonitor PERFORMANCE_MONITOR_INSTANCE = new PerformanceMonitor();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800234
235 @Deprecated
236 public static Measurement start(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700237 return PERFORMANCE_MONITOR_INSTANCE.startStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800238 }
239
240 @Deprecated
241 public static void stop(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700242 PERFORMANCE_MONITOR_INSTANCE.stopStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800243 }
244
245 @Deprecated
246 public static void clear() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700247 PERFORMANCE_MONITOR_INSTANCE.reset();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800248 }
249
250 @Deprecated
251 public static void report() {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700252 PERFORMANCE_MONITOR_INSTANCE.reportAll();
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800253 }
254
255 @Deprecated
256 public static void report(String tag) {
Ray Milkey5c9f2db2014-04-09 10:31:21 -0700257 PERFORMANCE_MONITOR_INSTANCE.reportStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800258 }
259
Ray Milkey45614c52014-04-07 16:30:54 -0700260 public static void main(String[] args) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700261 // test the measurement overhead
262 String tag;
263 for (int i = 0; i < 2; i++) {
264 tag = "foo foo foo";
265 Measurement m;
266 m = start(tag);
267 System.out.println(tag);
268 m.stop();
269 m = start(tag);
270 System.out.println(tag);
271 m.stop();
272 m = start(tag);
273 System.out.println(tag);
274 m.stop();
275 m = start(tag);
276 System.out.println(tag);
277 m.stop();
278 tag = "bar";
279 start(tag);
280 stop(tag);
281 tag = "baz";
282 start(tag);
283 stop(tag);
284 report();
285 clear();
286 }
287 for (int i = 0; i < 100; i++) {
288 tag = "a";
289 start(tag);
290 stop(tag);
291 start(tag);
292 stop(tag);
brian6df88232014-01-14 17:05:07 -0800293
Ray Milkey269ffb92014-04-03 14:43:30 -0700294 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);
304 start(tag);
305 stop(tag);
brian6df88232014-01-14 17:05:07 -0800306
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 tag = "b";
308 start(tag);
309 stop(tag);
310 tag = "c";
311 start(tag);
312 stop(tag);
313 report();
314 clear();
315 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800316 }
317}