blob: fa0553647206f9985f5530f313c1f52eaf235054 [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 {
Brian O'Connor8fdfb062014-01-08 17:47:57 -080015 private final static Logger log = LoggerFactory.getLogger(PerformanceMonitor.class);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080016
17 // experiment name -> PerformanceMonitor
Ray Milkey269ffb92014-04-03 14:43:30 -070018 private static final ConcurrentHashMap<String, PerformanceMonitor> perfMons = new ConcurrentHashMap<>();
19
Yuta HIGUCHI8f281452014-02-18 16:08:04 -080020 public static PerformanceMonitor experiment(String name) {
Ray Milkey269ffb92014-04-03 14:43:30 -070021 PerformanceMonitor pm = perfMons.get(name);
22 if (pm == null) {
23 pm = new PerformanceMonitor();
24 PerformanceMonitor existing = perfMons.putIfAbsent(name, pm);
25 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;
Brian O'Connor2daf7a92014-01-14 11:26:35 -080036 private final static double normalization = Math.pow(10, 6);
37
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>();
54 Queue<Measurement> existing_list = map.putIfAbsent(tag, list);
55 if (existing_list != null) {
56 // someone concurrently added, using theirs
57 list = existing_list;
58 }
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() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 String result = "Performance Results: (avg/start/stop/count)\n";
101 if (map.size() == 0) {
102 result += "No Measurements";
103 log.error(result);
104 return;
105 }
106 long experimentEnd = -1;
107 for (Entry<String, Queue<Measurement>> e : map.entrySet()) {
108 String key = e.getKey();
109 Queue<Measurement> list = e.getValue();
110 long total = 0, count = 0;
111 long start = Long.MAX_VALUE, stop = -1;
112 for (Measurement m : list) {
113 if (m.stop < 0) {
114 continue; // measurement has not been stopped
115 }
116 // Collect overall start and end times
117 if (m.start < start) {
118 start = m.start;
119 }
120 if (m.stop > stop) {
121 stop = m.stop;
122 if (stop > experimentEnd) {
123 experimentEnd = stop;
124 }
125 }
126 // Collect statistics for average
127 total += m.elapsed();
128 count++;
129 }
130 double avg = (double) total / count;
131 // Normalize start/stop
132 start -= experimentStart;
133 stop -= experimentStart;
134 result += key + '=' +
135 (avg / normalization) + '/' +
136 (start / normalization) + '/' +
137 (stop / normalization) + '/' +
138 count + '\n';
139 }
140 double overheadMs = overhead / normalization;
141 double experimentElapsed = (experimentEnd - experimentStart) / normalization;
142 result += "TotalTime:" + experimentElapsed + "/Overhead:" + overheadMs;
143 log.error(result);
144// log.error("Performance Results: {} with measurement overhead: {} ms", map, overheadMilli);
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800145 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800146
147 /**
148 * Write the performance measurement for a tag to the log
149 *
150 * @param tag the tag name.
151 */
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800152 public void reportStep(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700153 Queue<Measurement> list = map.get(tag);
154 if (list == null) {
155 return; //TODO
156 }
157 //TODO: fix this;
158 Measurement m = list.peek();
159 if (m != null) {
160 log.error("Performance Result: tag = {} start = {} stop = {} elapsed = {}",
161 tag, m.start, m.stop, m.toString());
162 } else {
163 log.error("Performance Result: unknown tag {}", tag);
164 }
Pavlin Radoslavovb5e31782014-01-12 20:11:46 -0800165 }
166
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800167 /**
168 * A single performance measurement
169 */
Brian O'Connor2daf7a92014-01-14 11:26:35 -0800170 public static class Measurement {
Ray Milkey269ffb92014-04-03 14:43:30 -0700171 long start;
172 long stop = -1;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800173
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 /**
175 * Start the measurement
176 */
177 public void start() {
178 if (start <= 0) {
179 start = System.nanoTime();
180 }
181 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800182
Ray Milkey269ffb92014-04-03 14:43:30 -0700183 /**
184 * Stop the measurement
185 */
186 public void stop() {
187 long now = System.nanoTime();
188 stop(now);
189 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800190
Ray Milkey269ffb92014-04-03 14:43:30 -0700191 /**
192 * Stop the measurement at a specific time
193 *
194 * @param time to stop
195 */
196 public void stop(long time) {
197 if (stop <= 0) {
198 stop = time;
199 }
200 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800201
Ray Milkey269ffb92014-04-03 14:43:30 -0700202 /**
203 * Compute the elapsed time of the measurement in nanoseconds
204 *
205 * @return the measurement time in nanoseconds, or -1 if the measurement is stil running.
206 */
207 public long elapsed() {
208 if (stop <= 0) {
209 return -1;
210 } else {
211 return stop - start;
212 }
213 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800214
Ray Milkey269ffb92014-04-03 14:43:30 -0700215 /**
216 * Returns the number of milliseconds for the measurement as a String.
217 */
218 @Override
219 public String toString() {
220 double milli = elapsed() / normalization;
221 double startMs = start / normalization;
222 double stopMs = stop / normalization;
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800223
Ray Milkey269ffb92014-04-03 14:43:30 -0700224 return milli + "ms/" + startMs + '/' + stopMs;
225 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800226 }
Yuta HIGUCHIa7188f22014-02-18 12:48:06 -0800227
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800228 @Deprecated
229 private static final PerformanceMonitor theInstance = new PerformanceMonitor();
230
231 @Deprecated
232 public static Measurement start(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700233 return theInstance.startStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800234 }
235
236 @Deprecated
237 public static void stop(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700238 theInstance.stopStep(tag);
239 ;
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800240 }
241
242 @Deprecated
243 public static void clear() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700244 theInstance.reset();
245 ;
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800246 }
247
248 @Deprecated
249 public static void report() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700250 theInstance.reportAll();
251 ;
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800252 }
253
254 @Deprecated
255 public static void report(String tag) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700256 theInstance.reportStep(tag);
Yuta HIGUCHI8f281452014-02-18 16:08:04 -0800257 }
258
Ray Milkey45614c52014-04-07 16:30:54 -0700259 public static void main(String[] args) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700260 // test the measurement overhead
261 String tag;
262 for (int i = 0; i < 2; i++) {
263 tag = "foo foo foo";
264 Measurement m;
265 m = start(tag);
266 System.out.println(tag);
267 m.stop();
268 m = start(tag);
269 System.out.println(tag);
270 m.stop();
271 m = start(tag);
272 System.out.println(tag);
273 m.stop();
274 m = start(tag);
275 System.out.println(tag);
276 m.stop();
277 tag = "bar";
278 start(tag);
279 stop(tag);
280 tag = "baz";
281 start(tag);
282 stop(tag);
283 report();
284 clear();
285 }
286 for (int i = 0; i < 100; i++) {
287 tag = "a";
288 start(tag);
289 stop(tag);
290 start(tag);
291 stop(tag);
brian6df88232014-01-14 17:05:07 -0800292
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 start(tag);
294 stop(tag);
295 start(tag);
296 stop(tag);
297 start(tag);
298 stop(tag);
299 start(tag);
300 stop(tag);
301 start(tag);
302 stop(tag);
303 start(tag);
304 stop(tag);
brian6df88232014-01-14 17:05:07 -0800305
Ray Milkey269ffb92014-04-03 14:43:30 -0700306 tag = "b";
307 start(tag);
308 stop(tag);
309 tag = "c";
310 start(tag);
311 stop(tag);
312 report();
313 clear();
314 }
Brian O'Connor8fdfb062014-01-08 17:47:57 -0800315 }
316}