Merge lp:~glcompbench-dev/glcompbench/manmower-options-3 into lp:glcompbench
- manmower-options-3
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandros Frantzis | Pending | ||
Review via email: mp+104087@code.launchpad.net |
Commit message
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-
* 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.
- 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.
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:
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.
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:
> 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
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:/
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.
Derek Foreman (derek-foreman) wrote : | # |
> Based on your patch I have implemented what is essentially a more generic
> version of your proposal:
>
> https:/
> 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
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 |
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.