Merge lp:~glcompbench-dev/glcompbench/manmower-options-3 into lp:glcompbench

Proposed by Marc Ordinas i Llopis
Status: Needs review
Proposed branch: lp:~glcompbench-dev/glcompbench/manmower-options-3
Merge into: lp:glcompbench
Diff against target: 703 lines (+336/-54)
7 files modified
src/composite-canvas-egl.cc (+3/-2)
src/composite-canvas.cc (+42/-8)
src/glcompbench.cc (+39/-0)
src/options.cc (+63/-4)
src/options.h (+19/-1)
src/profiler.cc (+153/-39)
src/profiler.h (+17/-0)
To merge this branch: bzr merge lp:~glcompbench-dev/glcompbench/manmower-options-3
Reviewer Review Type Date Requested Status
Alexandros Frantzis Pending
Review via email: mp+104087@code.launchpad.net

Description of the change

* Allow tracking both "total" and "subset" collections of profiling data.
* Print out full run statistics at the end of execution.
* Add an option to enable vsync and enable it for composite-canvas-egl.
* Add an extended stats command line option to print min and max profiling information.
* Allow specifying size and position with an X geometry string.
* Add pass/fail testing.
* Add standard deviation.

Also reverted option parsing changes from previous submission.

To post a comment you must log in.
90. By Marc Ordinas i Llopis

Options: Call eglSwapInterval only if vsync wasn't enabled.

91. By Marc Ordinas i Llopis

Style: Move standard deviation calculation to extended_stats code path.

92. By Marc Ordinas i Llopis

Style: Use Util::split to separate points instead of strtok.

Revision history for this message
Marc Ordinas i Llopis (marcoil) wrote :

Based on suggestions by Jesse, I've pushed some changes:
* Call eglSwapInterval only if --enable-vsync wasn't requestes.
* Moved standard deviation variable to the scope it's used in.
* Use Util::split in libmatrix instead of strtok.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

I am still confused about why we need to have multiple substats. In the current code base the SUBSET group ends up holding exactly the same data as the TOTAL group (i.e. Profile::sample_point() samples both substat groups and it's the only public method to sample points).

In case such functionality is indeed needed, I believe that it could be achieved more cleanly by using multiple Profiler instances to hold different data sets.

Revision history for this message
Derek Foreman (derek-foreman) wrote :

> I am still confused about why we need to have multiple substats. In the
> current code base the SUBSET group ends up holding exactly the same data as
> the TOTAL group (i.e. Profile::sample_point() samples both substat groups and
> it's the only public method to sample points).

The subsets are temporal - the TOTAL group is quite different from SUBSET after the first iteration. The SUBSET group is what was previously tracked.

The TOTAL group is what I use for pass/fail tests in later commits in the series.

Both contain results accumulated from the same profiler points, but TOTAL is accumulated over the total duration of a test.

> In case such functionality is indeed needed, I believe that it could be
> achieved more cleanly by using multiple Profiler instances to hold different
> data sets.

I think that would result in more convoluted code everywhere that actually uses a profiler point - doubling up every profiler point occurrence via cut and paste, and making some of the resets conditional.

If that's what you'd prefer, I can write it that way instead... Please let me know

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Thanks for the explanation, it makes much more sense now. You are right that for this use case using separate Profiler instances would be messy.

Based on your patch I have implemented what is essentially a more generic version of your proposal:

https://code.launchpad.net/~linaro-graphics-wg/glcompbench/profiler-stat-collection

The current code in the branch is not complete yet. It only tracks per-iteration statistics, but adding per-test and total statistics is easy. The code is also missing the 'squares' stats datum, but this is trivial to add, too.

In your proposal the total stats are displayed after each test, but they are not really per-test stats, which I find somewhat confusing. I think it would be better to show per-test stats after each test is completed and total stats only once at the end.

Also, I would rather keep the accumulation logic outside of the profiler class. It's easy to calculate accumulated data outside the profiler, given the statistical data in the 'total' collection (calculating std. dev. needs a bit more care).

What do you think?

Meanwhile I will merge into trunk some of the other changes that don't depend on the substats infrastructure.

Revision history for this message
Derek Foreman (derek-foreman) wrote :

> Based on your patch I have implemented what is essentially a more generic
> version of your proposal:
>
> https://code.launchpad.net/~linaro-graphics-wg/glcompbench/profiler-stat-
> collection

Very cool :)

> The current code in the branch is not complete yet. It only tracks per-
> iteration statistics, but adding per-test and total statistics is easy. The
> code is also missing the 'squares' stats datum, but this is trivial to add,
> too.
>
> In your proposal the total stats are displayed after each test, but they are
> not really per-test stats, which I find somewhat confusing. I think it would
> be better to show per-test stats after each test is completed and total stats
> only once at the end.

Yes, that sounds quite sensible.

> Also, I would rather keep the accumulation logic outside of the profiler
> class. It's easy to calculate accumulated data outside the profiler, given the
> statistical data in the 'total' collection (calculating std. dev. needs a bit
> more care).
>
> What do you think?

It's not immediately obvious to me how I'd calculate std. dev. anymore. I could easily calculate the std. dev. of individual profiler points, but since there aren't the same number of each profiler point in an iteration, I can't figure out how to calculate std. dev. of test iterations, which is the more useful stat from my perspective.

Though perhaps there's a way that I'm just not seeing?

> Meanwhile I will merge into trunk some of the other changes that don't depend
> on the substats infrastructure.

Thanks!

Unmerged revisions

92. By Marc Ordinas i Llopis

Style: Use Util::split to separate points instead of strtok.

91. By Marc Ordinas i Llopis

Style: Move standard deviation calculation to extended_stats code path.

90. By Marc Ordinas i Llopis

Options: Call eglSwapInterval only if vsync wasn't enabled.

89. By Derek Foreman

add standard deviation

88. By Derek Foreman

Add pass/fail testing

87. By Derek Foreman

Allow specifying size and position with an X geometry string

86. By Derek Foreman

Add an extended stats command line option to print min and max profiling information

85. By Derek Foreman

Add an option to enable vsync and enable it for composite-canvas-egl

84. By Derek Foreman

Print out full run statistics at the end of execution

83. By Derek Foreman

Allow tracking both "total" and "subset" collections of profiling data

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/composite-canvas-egl.cc'
2--- src/composite-canvas-egl.cc 2012-04-17 23:19:42 +0000
3+++ src/composite-canvas-egl.cc 2012-05-02 14:54:19 +0000
4@@ -247,8 +247,9 @@
5 return false;
6 }
7
8- if (!eglSwapInterval(egl_display_, 0))
9- Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
10+ if (!Options::vsync)
11+ if (!eglSwapInterval(egl_display_, 0))
12+ Log::info("** Failed to set swap interval. Results may be bounded above by refresh rate.\n");
13
14 use_accel_tfp_ = Options::use_accel_tfp;
15
16
17=== modified file 'src/composite-canvas.cc'
18--- src/composite-canvas.cc 2012-04-19 14:15:59 +0000
19+++ src/composite-canvas.cc 2012-05-02 14:54:19 +0000
20@@ -215,7 +215,7 @@
21 attr.event_mask = StructureNotifyMask | ExposureMask;
22 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
23
24- win = XCreateWindow(xdpy_, root_, 0, 0, width, height,
25+ win = XCreateWindow(xdpy_, root_, x, y, width, height,
26 0, vis_info->depth, InputOutput,
27 vis_info->visual, mask, &attr);
28
29@@ -226,7 +226,9 @@
30 sizehints.y = y;
31 sizehints.width = width;
32 sizehints.height = height;
33- sizehints.flags = USSize | USPosition;
34+ sizehints.flags = USSize;
35+ if (Options::specify_position)
36+ sizehints.flags |= USPosition;
37 XSetNormalHints(xdpy_, win, &sizehints);
38 XSetStandardProperties(xdpy_, win, name, name,
39 None, NULL, 0, &sizehints);
40@@ -474,6 +476,8 @@
41 CompositeCanvas::profiler_screen_update_pair();
42
43 Profiler::instance().reset();
44+ if (Options::test_arg)
45+ Profiler::instance().init_test(Options::test_arg);
46 CompositeCanvas::profiler_cycle_pair().sample_start();
47 }
48
49@@ -487,6 +491,8 @@
50 Profiler &profiler = Profiler::instance();
51
52 CompositeCanvas::profiler_cycle_pair().sample_end();
53+ profiler.accumulator_end();
54+ profiler.accumulator_start();
55 CompositeCanvas::profiler_cycle_pair().sample_start();
56
57 CompositeCanvas::profiler_cycle_pair().get_stats(stats);
58@@ -499,9 +505,18 @@
59
60 for (Profiler::Point p = 0; p < profiler.get_num_points(); p += 2) {
61 profiler.get_stats(p, p + 1, stats);
62- Log::info("Operation %s => Avg: %f Total: %f Samples: %llu\n",
63- profiler.get_name_by_point(p),
64- stats.average, stats.total, stats.nsamples);
65+ if (Options::extended_stats) {
66+ double sd = 0.0;
67+ if (stats.nsamples)
68+ sd = sqrt(stats.squares/(stats.nsamples-1));
69+ Log::info("Operation %s => Min: %f Avg: %f Max: %f Sd: %f Total: %f Samples: %llu\n",
70+ profiler.get_name_by_point(p),
71+ stats.low, stats.average, stats.high, sd, stats.total,
72+ stats.nsamples);
73+ } else
74+ Log::info("Operation %s => Avg: %f Total: %f Samples: %llu\n",
75+ profiler.get_name_by_point(p),
76+ stats.average, stats.total, stats.nsamples);
77 }
78
79 profiler.reset();
80@@ -511,6 +526,24 @@
81 }
82 iterations_ = 0;
83 iteration_complete = true;
84+
85+ Log::info("Final statistics:\n");
86+
87+ for (Profiler::Point p = 0; p < profiler.get_num_points(); p += 2) {
88+ profiler.get_stats(p, p + 1, stats, Profiler::TOTAL);
89+ if (Options::extended_stats) {
90+ double sd = 0;
91+ if (stats.nsamples)
92+ sd = sqrt(stats.squares/(stats.nsamples-1));
93+ Log::info("Operation %s => Min: %f Avg: %f Max: %f Sd: %f Total: %f Samples: %llu\n",
94+ profiler.get_name_by_point(p),
95+ stats.low, stats.average, stats.high, sd, stats.total,
96+ stats.nsamples);
97+ } else
98+ Log::info("Operation %s => Avg: %f Total: %f Samples: %llu\n",
99+ profiler.get_name_by_point(p),
100+ stats.average, stats.total, stats.nsamples);
101+ }
102 }
103 }
104
105@@ -566,9 +599,10 @@
106 return false;
107
108 /* Create the X window we are going to draw on */
109- width_ = Options::size;
110- height_ = Options::size;
111- canvas_ = create_canvas_x_window(glcompbench_version_string.c_str(), 0, 0,
112+ width_ = Options::w;
113+ height_ = Options::h;
114+ canvas_ = create_canvas_x_window(glcompbench_version_string.c_str(),
115+ Options::x, Options::y,
116 width_, height_,
117 vis_info);
118 XFree(vis_info);
119
120=== modified file 'src/glcompbench.cc'
121--- src/glcompbench.cc 2012-03-20 16:32:54 +0000
122+++ src/glcompbench.cc 2012-05-02 14:54:19 +0000
123@@ -41,6 +41,7 @@
124 #include "options.h"
125 #include "log.h"
126 #include "util.h"
127+#include "profiler.h"
128
129 static const char *default_benchmarks[] = {
130 "default",
131@@ -276,5 +277,43 @@
132 #ifdef USE_GLPROXY
133 glProxyClose(proxy_context);
134 #endif
135+
136+ if (Options::test) {
137+ double stat = 0.0;
138+ double sd = 0.0;
139+ Profiler &profiler = Profiler::instance();
140+ Profiler::Stats stats;
141+ profiler.get_accumulated_stats(stats);
142+ if (stats.nsamples == 0) {
143+ Log::info("No samples in test subset: TEST FAILED\n");
144+ return 1;
145+ }
146+ sd = sqrt(stats.squares/(stats.nsamples-1));
147+ Log::info("Test subset: Min: %f Avg: %f Max: %f Sd: %f Total: %f Samples: %llu\n",
148+ stats.low, stats.average, stats.high, sd, stats.total, stats.nsamples);
149+ switch (Options::test_stat) {
150+ case Options::STAT_AVERAGE:
151+ stat = stats.average;
152+ break;
153+ case Options::STAT_MINIMUM:
154+ stat = stats.low;
155+ break;
156+ case Options::STAT_MAXIMUM:
157+ stat = stats.high;
158+ break;
159+ case Options::STAT_TOTAL:
160+ stat = stats.total;
161+ break;
162+ case Options::STAT_NONE:
163+ break;
164+ }
165+ if (stat > Options::test_limit) {
166+ Log::info("Test statistic value %f > tolerance %f : TEST FAILED\n",
167+ stat, Options::test_limit);
168+ return 1;
169+ } else
170+ Log::info("Test statistic value %f <= tolerance %f : TEST PASSED\n",
171+ stat, Options::test_limit);
172+ }
173 return 0;
174 }
175
176=== modified file 'src/options.cc'
177--- src/options.cc 2012-02-17 11:51:23 +0000
178+++ src/options.cc 2012-05-02 14:54:19 +0000
179@@ -23,6 +23,7 @@
180 */
181
182 #include <X11/Xlib.h>
183+#include <X11/Xutil.h>
184 #include <cstring>
185 #include <cstdlib>
186 #include <cstdio>
187@@ -30,7 +31,6 @@
188
189 #include "options.h"
190
191-int Options::size = 512;
192 std::list<Window> Options::window_ids;
193 bool Options::run_forever = false;
194 bool Options::use_accel_tfp = true;
195@@ -43,6 +43,18 @@
196 bool Options::show_help = false;
197 bool Options::list_tests = false;
198 bool Options::swap_buffers = true;
199+bool Options::vsync = false;
200+bool Options::extended_stats = false;
201+int Options::x = 0;
202+int Options::y = 0;
203+unsigned int Options::w = 512;
204+unsigned int Options::h = 512;
205+bool Options::specify_position = false;
206+bool Options::test = false;
207+double Options::test_limit = 0.0;
208+char *Options::test_arg = NULL;
209+Options::Statistics Options::test_stat = STAT_NONE;
210+
211 #ifdef USE_GLPROXY
212 Options::Backends Options::backend = BACKEND_EGL_ES2;
213 #elif defined(USE_GLX)
214@@ -68,6 +80,12 @@
215 {"debug", 0, 0, 0},
216 {"help", 0, 0, 0},
217 {"backend", 1, 0, 0},
218+ {"enable-vsync", no_argument, 0, 0},
219+ {"extended-stats", no_argument, 0, 0},
220+ {"geometry", required_argument, 0, 0},
221+ {"test-points", required_argument, 0, 0},
222+ {"test-limit", required_argument, 0, 0},
223+ {"test-stat", required_argument, 0, 0},
224 {0, 0, 0, 0}
225 };
226
227@@ -113,6 +131,20 @@
228 return Options::BACKEND_NONE;
229 }
230
231+static Options::Statistics
232+parse_statistic(const char *str)
233+{
234+ if (!strcmp("average", str))
235+ return Options::STAT_AVERAGE;
236+ else if (!strcmp("minimum", str))
237+ return Options::STAT_MINIMUM;
238+ else if (!strcmp("maximum", str))
239+ return Options::STAT_MAXIMUM;
240+ else if (!strcmp("total", str))
241+ return Options::STAT_TOTAL;
242+ return Options::STAT_NONE;
243+}
244+
245 void
246 Options::print_help()
247 {
248@@ -136,6 +168,13 @@
249 " back buffer, use glFinish() instead\n"
250 " --backend BACKEND The OpenGL backend to use (%s)\n"
251 " -d, --debug Display debug messages\n"
252+ " --enable-vsync Enable vertical refresh synchronization\n"
253+ " --extended-stats Output verbose profiling statistics\n"
254+ " --geometry GEOM Place window using an X geometry string\n"
255+ "Pass/Fail exit status tests:\n"
256+ " --test-points A comma separated list of profiler points to accumulate\n"
257+ " --test-stat Statistic for testing (average, minimum, maximum, total)\n"
258+ " --test-limit upper bound for test statistic\n"
259 " -h, --help Display help\n",
260 available_backends);
261 }
262@@ -143,6 +182,7 @@
263 bool
264 Options::parse_args(int argc, char **argv)
265 {
266+ int bits;
267 while (1) {
268 int option_index = -1;
269 int c;
270@@ -160,9 +200,10 @@
271
272 if (c == 'i' || !strcmp(optname, "ids"))
273 parse_window_ids(optarg, Options::window_ids);
274- else if (c == 's' || !strcmp(optname, "size"))
275- Options::size = strtol(optarg, NULL, 10);
276- else if (!strcmp(optname, "no-accel-tfp"))
277+ else if (c == 's' || !strcmp(optname, "size")) {
278+ Options::w = strtol(optarg, NULL, 10);
279+ Options::h = Options::w;
280+ } else if (!strcmp(optname, "no-accel-tfp"))
281 Options::use_accel_tfp = false;
282 else if (c == 'b' || !strcmp(optname, "benchmark"))
283 Options::benchmarks.push_back(optarg);
284@@ -186,6 +227,24 @@
285 Options::show_help = true;
286 else if (!strcmp(optname, "backend"))
287 Options::backend = parse_backend(optarg);
288+ else if (!strcmp(optname, "enable-vsymc"))
289+ Options::vsync = true;
290+ else if (!strcmp(optname, "extended-stats"))
291+ extended_stats = true;
292+ else if (!strcmp(optname, "geometry")) {
293+ bits = XParseGeometry(optarg, &x, &y, &w, &h);
294+ if ((bits & XValue) || (bits & YValue))
295+ specify_position = true;
296+ } else if (!strcmp(optname, "test-points")) {
297+ Options::test_arg = optarg;
298+ Options::test = true;
299+ } else if (!strcmp(optname, "test-limit")) {
300+ Options::test_limit = strtod(optarg, NULL);
301+ Options::test = true;
302+ } else if (!strcmp(optname, "test-stat")) {
303+ Options::test_stat = parse_statistic(optarg);
304+ Options::test = true;
305+ }
306 }
307
308 return true;
309
310=== modified file 'src/options.h'
311--- src/options.h 2011-12-12 13:16:47 +0000
312+++ src/options.h 2012-05-02 14:54:19 +0000
313@@ -34,7 +34,6 @@
314 static void print_help();
315
316 static std::list<Window> window_ids;
317- static int size;
318 static bool run_forever;
319 static bool use_accel_tfp;
320 static std::list<std::string> benchmarks;
321@@ -46,6 +45,16 @@
322 static bool show_help;
323 static bool list_tests;
324 static bool swap_buffers;
325+ static bool vsync;
326+ static bool extended_stats;
327+ static int x;
328+ static int y;
329+ static unsigned int w;
330+ static unsigned int h;
331+ static bool specify_position;
332+ static bool test;
333+ static double test_limit;
334+ static char *test_arg;
335
336 typedef enum {
337 BACKEND_NONE,
338@@ -54,6 +63,15 @@
339 BACKEND_EGL_ES2
340 } Backends;
341 static Backends backend;
342+
343+ typedef enum {
344+ STAT_NONE,
345+ STAT_AVERAGE,
346+ STAT_MINIMUM,
347+ STAT_MAXIMUM,
348+ STAT_TOTAL
349+ } Statistics;
350+ static Statistics test_stat;
351 };
352
353 #endif /* OPTIONS_H_ */
354
355=== modified file 'src/profiler.cc'
356--- src/profiler.cc 2011-10-14 12:41:30 +0000
357+++ src/profiler.cc 2012-05-02 14:54:19 +0000
358@@ -22,40 +22,58 @@
359 */
360
361 #define __STDC_LIMIT_MACROS
362+#include <string.h>
363 #include <sys/time.h>
364 #include <stdint.h>
365 #include <string>
366 #include <vector>
367
368 #include "profiler.h"
369+#include "util.h"
370
371 struct ProfilerPoint
372 {
373+ struct substats {
374+ uint64_t last_sample;
375+ uint64_t nsamples;
376+ uint64_t low;
377+ uint64_t high;
378+ uint64_t total;
379+ double average;
380+ double squares;
381+ } substats[Profiler::NUM_DURATIONS];
382+
383 ProfilerPoint(const char *name_):
384 name(name_), prev(0), next(0)
385 {
386- reset();
387+ full_reset();
388+ }
389+
390+ void reset(Profiler::duration d)
391+ {
392+ substats[d].last_sample = 0;
393+ substats[d].nsamples = 0;
394+ substats[d].low = UINT64_MAX;
395+ substats[d].high = 0;
396+ substats[d].total = 0;
397+ substats[d].average = 0.0;
398+ substats[d].squares = 0.0;
399 }
400
401 void reset()
402 {
403- last_sample = 0;
404- nsamples = 0;
405- low = UINT64_MAX;
406- high = 0;
407- total = 0;
408- average = 0.0;
409+ reset(Profiler::SUBSET);
410+ }
411+
412+ void full_reset()
413+ {
414+ reset(Profiler::SUBSET);
415+ reset(Profiler::TOTAL);
416 }
417
418 std::string name;
419 ProfilerPoint *prev;
420 ProfilerPoint *next;
421- uint64_t last_sample;
422- uint64_t nsamples;
423- uint64_t low;
424- uint64_t high;
425- uint64_t total;
426- double average;
427 };
428
429 Profiler::~Profiler()
430@@ -81,11 +99,18 @@
431 Profiler::Point
432 Profiler::register_point(const char *name)
433 {
434+ Profiler::Point out;
435 ProfilerPoint *point = new ProfilerPoint(name);
436
437 points_.push_back(point);
438-
439- return points_.size() - 1;
440+ out = points_.size() - 1;
441+ accumulated_stats.nsamples = 0;
442+ accumulated_stats.low = UINT64_MAX;
443+ accumulated_stats.high = 0;
444+ accumulated_stats.total = 0;
445+ accumulated_stats.average = 0;
446+ accumulated_stats.squares = 0;
447+ return out;
448 }
449
450 /**
451@@ -153,13 +178,13 @@
452 * @return the found point or NULL
453 */
454 static ProfilerPoint *
455-get_prev_sampled_point(ProfilerPoint *point)
456+get_prev_sampled_point(ProfilerPoint *point, Profiler::duration d)
457 {
458 ProfilerPoint *cur = point->prev;
459- while (cur != NULL && cur->last_sample == 0 && cur != point)
460+ while (cur != NULL && cur->substats[d].last_sample == 0 && cur != point)
461 cur = cur->prev;
462
463- if (cur != NULL && cur->last_sample != 0)
464+ if (cur != NULL && cur->substats[d].last_sample != 0)
465 return cur;
466 else
467 return NULL;
468@@ -175,28 +200,45 @@
469 bool
470 Profiler::sample_point(Profiler::Point p)
471 {
472+ uint64_t now = get_timestamp_us();
473+
474 if (!is_valid_point(p))
475 return false;
476
477+ sample_point(p, TOTAL, now);
478+ sample_point(p, SUBSET, now);
479+
480+ return true;
481+}
482+
483+void
484+Profiler::sample_point(Profiler::Point p, Profiler::duration d, uint64_t now)
485+{
486 ProfilerPoint *point = points_[p];
487- ProfilerPoint *prev = get_prev_sampled_point(point);
488- uint64_t now = get_timestamp_us();
489+ ProfilerPoint *prev = get_prev_sampled_point(point, d);
490
491 if (prev != NULL) {
492- uint64_t diff = now - point->prev->last_sample;
493- point->average = (point->nsamples * point->average + diff) /
494- (point->nsamples + 1);
495- point->total += diff;
496- if (diff < point->low)
497- point->low = diff;
498- if (diff > point->high)
499- point->high = diff;
500- point->nsamples++;
501+ double oldmean = point->substats[d].average;
502+ uint64_t diff = now - point->prev->substats[d].last_sample;
503+ if ((d == TOTAL) && (1 << p) & test_mask) {
504+ accumulator_active = true;
505+ accumulator += diff;
506+ }
507+ point->substats[d].average =
508+ (point->substats[d].nsamples *
509+ point->substats[d].average + diff) /
510+ (point->substats[d].nsamples + 1);
511+ point->substats[d].squares += (diff/1000.0 - oldmean/1000.0) *
512+ (diff/1000.0 - point->substats[d].average/1000.0);
513+ point->substats[d].total += diff;
514+ if (diff < point->substats[d].low)
515+ point->substats[d].low = diff;
516+ if (diff > point->substats[d].high)
517+ point->substats[d].high = diff;
518+ point->substats[d].nsamples++;
519 }
520
521- point->last_sample = now;
522-
523- return true;
524+ point->substats[d].last_sample = now;
525 }
526
527 /**
528@@ -205,13 +247,15 @@
529 * @param p1 the first point
530 * @param p2 the second point (may be the same as the first for a complete loop)
531 * @param stats the struct to fill
532+ * @param duration optionally SUBSET or TOTAL to choose a stats group
533 *
534 * @return whether the operation succeeded
535 */
536 bool
537 Profiler::get_stats(Profiler::Point p1,
538 Profiler::Point p2,
539- Stats &stats)
540+ Stats &stats,
541+ enum duration d)
542 {
543 if (!is_valid_point(p1) || !is_valid_point(p2))
544 return false;
545@@ -225,18 +269,21 @@
546 stats.high = 0.0;
547 stats.total = 0.0;
548 stats.average = 0.0;
549+ stats.squares = 0.0;
550
551 /* Go from p1 to p2, gathering stats */
552 while (cur != NULL) {
553 /*
554 * We store values as microseconds internally, but provide them as
555- * milliseconds
556+ * milliseconds, except for squares which is already in milliseconds
557 */
558- stats.average += cur->average / 1000.0;
559- stats.total += cur->total / 1000.0;
560- stats.low += cur->low / 1000.0;
561- stats.high += cur->high / 1000.0;
562- stats.nsamples = std::max(stats.nsamples, cur->nsamples);
563+ stats.average += cur->substats[d].average / 1000.0;
564+ stats.squares += cur->substats[d].squares;
565+ stats.total += cur->substats[d].total / 1000.0;
566+ stats.low += cur->substats[d].low / 1000.0;
567+ stats.high += cur->substats[d].high / 1000.0;
568+ stats.nsamples = std::max(stats.nsamples,
569+ cur->substats[d].nsamples);
570 if (cur == end)
571 break;
572 cur = cur->next;
573@@ -245,6 +292,25 @@
574 return (cur == end);
575 }
576
577+bool
578+Profiler::get_stats(Profiler::Point p1,
579+ Profiler::Point p2,
580+ Stats &stats)
581+{
582+ return get_stats(p1, p2, stats, SUBSET);
583+}
584+
585+void
586+Profiler::get_accumulated_stats(Stats &stats)
587+{
588+ stats.average = accumulated_stats.average / 1000.0;
589+ stats.squares = accumulated_stats.squares;
590+ stats.total = accumulated_stats.total / 1000.0;
591+ stats.low = accumulated_stats.low / 1000.0;
592+ stats.high = accumulated_stats.high / 1000.0;
593+ stats.nsamples = accumulated_stats.nsamples;
594+}
595+
596 /**
597 * Resets the benchmarking info of a profiling point.
598 *
599@@ -297,3 +363,51 @@
600 return now;
601 }
602
603+void
604+Profiler::accumulator_start()
605+{
606+ accumulator_active = false;
607+ accumulator = 0;
608+}
609+
610+void
611+Profiler::accumulator_end()
612+{
613+ if (!accumulator_active)
614+ return;
615+
616+ accumulator_active = false;
617+ double oldmean = accumulated_stats.average;
618+ accumulated_stats.average =
619+ (accumulated_stats.nsamples *
620+ accumulated_stats.average + accumulator) /
621+ (accumulated_stats.nsamples + 1);
622+ accumulated_stats.squares = accumulated_stats.squares +
623+ (accumulator/1000.0 - oldmean/1000.0) *
624+ (accumulator/1000.0 - accumulated_stats.average/1000.0);
625+
626+ accumulated_stats.total += accumulator;
627+ if (accumulator < accumulated_stats.low)
628+ accumulated_stats.low = accumulator;
629+ if (accumulator > accumulated_stats.high)
630+ accumulated_stats.high = accumulator;
631+ accumulated_stats.nsamples++;
632+ accumulator = 0;
633+}
634+
635+void
636+Profiler::init_test(char *str)
637+{
638+ test_mask = 0;
639+ vector<string> points;
640+
641+ Util::split(str, ',', points);
642+ for (vector<string>::iterator it = points.begin(); it != points.end(); ++it) {
643+ /* the +1 is because the points in the profiler pairs both have the same
644+ * name, and we want the second point, which actually has the stats...
645+ */
646+ Profiler::Point p = get_point_by_name((*it).c_str()) + 1;
647+ if (p > 0)
648+ test_mask |= 1 << p;
649+ }
650+}
651
652=== modified file 'src/profiler.h'
653--- src/profiler.h 2011-10-14 12:41:30 +0000
654+++ src/profiler.h 2012-05-02 14:54:19 +0000
655@@ -37,6 +37,12 @@
656 public:
657 typedef int Point;
658
659+ enum duration {
660+ SUBSET,
661+ TOTAL,
662+ NUM_DURATIONS
663+ };
664+
665 struct Stats
666 {
667 uint64_t nsamples;
668@@ -44,6 +50,7 @@
669 double high;
670 double total;
671 double average;
672+ double squares;
673 };
674
675 class PointPair
676@@ -132,10 +139,15 @@
677 bool connect(Point p1, Point p2);
678 bool sample_point(Point p);
679 bool get_stats(Point p1, Point p2, Stats &stats);
680+ bool get_stats(Point p1, Point p2, Stats &stats, enum duration d);
681 void reset_point(Point p);
682 void reset(void);
683 Point get_num_points() { return points_.size(); }
684 static uint64_t get_timestamp_us();
685+ void accumulator_start();
686+ void accumulator_end();
687+ void get_accumulated_stats(Stats &stats);
688+ void init_test(char *str);
689
690 /**
691 * Gets the profiler singleton
692@@ -147,8 +159,13 @@
693 }
694
695 private:
696+ void sample_point(Point p, enum duration d, uint64_t now);
697 bool is_valid_point(Point p);
698 std::vector<ProfilerPoint *> points_;
699+ Stats accumulated_stats;
700+ bool accumulator_active;
701+ unsigned int accumulator;
702+ unsigned int test_mask;
703 };
704
705

Subscribers

People subscribed via source and target branches

to all changes: