From 3c19a6413cf77d86d75501f531af08b13db1f411 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 29 Sep 2006 16:42:44 -0700 Subject: [PATCH] perf: Rework the suite to allow multiple performance tests to be defined in one file. --- perf/README | 66 ++++++++++++------- perf/cairo-perf.c | 161 +++++++++++++++++++++++++--------------------- perf/cairo-perf.h | 23 ++++--- perf/paint.c | 32 ++++++--- perf/tessellate.c | 22 +++++-- 5 files changed, 177 insertions(+), 127 deletions(-) diff --git a/perf/README b/perf/README index d577cd7f0..31dfc2f9b 100644 --- a/perf/README +++ b/perf/README @@ -25,8 +25,8 @@ Creating a new performance test This is where we could use everybody's help. If you have encountered a sequence of cairo operations that are slower than you would like, then please provide a performance test. Writing a test is very simple, it -requires you to write only a single function exercising the cairo -calls of interest. +requires you to write only a small C file with a couple of functions, +one of which exercises the cairo calls of interest. Here is the basic structure of a performance test file: @@ -36,42 +36,61 @@ Here is the basic structure of a performance test file: * Please copy the MIT blurb as in other tests */ - #include "cairo-perf.h" + #include "cairo-perf.h" - cairo_perf_ticks_t - my_new_test (cairo_t *cr, int width, int height) - { - /* First do any setup for which the execution time should not - * be measured. For example, this might include loading - * images from disk, creating patterns, etc. */ + static cairo_perf_ticks_t + do_my_new_test (cairo_t *cr, int width, int height) + { + /* First do any setup for which the execution time should not + * be measured. For example, this might include loading + * images from disk, creating patterns, etc. */ - cairo_perf_timer_start (); + cairo_perf_timer_start (); - /* Now make the real cairo calls to be measured */ + /* Now make the real cairo calls to be measured */ - cairo_perf_timer_stop (); + cairo_perf_timer_stop (); - /* Finally, any cleanup */ + /* Finally, any cleanup */ - /* Then return the time that elapsed. */ + /* Then return the time that elapsed. */ - return cairo_perf_timer_elapsed (); + return cairo_perf_timer_elapsed (); } -That's really all there is to writing a new test. Then, to fully -integrate this you just need to add your new test to three different -lists. (TODO: We should set this up better so that the lists are -maintained automatically---computed from the list of files in -cairo/perf, for example). Here's what needs to be added: + void + my_new_test (cairo_perf_t *perf) + { + cairo_perf_run (perf, "my_new_test", do_my_new_test); + } + +That's really all there is to writing a new test. The first function +above is the one that does the real work and returns a timing +number. The second function is the one that will be called by the +performance test rig (see below for how to accomplish that), and +allows for multiple performance cases to be written in one file, +(simply call cairo_perf_run once for each case, passing the +appropriate callback function to each). + +We go through this dance of indirectly calling your own function +through cairo_perf_run so that cairo_perf_run can call your function +many times and measure statistical properties over the many runs. + +Finally, to fully integrate your new test case you just need to add +your new test to three different lists. (TODO: We should set this up +better so that the lists are maintained automatically---computed from +the list of files in cairo/perf, for example). Here's what needs to be +added: 1. Makefile.am: Add the new file name to the cairo_perf_SOURCES list - 2. cairo-perf.h: Add a new CAIRO_PERF_DECL line with the name of your function + 2. cairo-perf.h: Add a new CAIRO_PERF_DECL line with the name of your + function, (my_new_test in the example above) 3. cairo-perf.c: Add a new row to the list at the end of the file. A typical entry would look like: - { "my_new_test", my_new_test, 16, 64 } + { my_new_test, 16, 64 } The last two numbers are a minimum and a maximum image size at which your test should be exercised. If these values are the same, @@ -81,6 +100,3 @@ cairo/perf, for example). Here's what needs to be added: 64x64. Thanks for your contributions and have fun with cairo! - - - diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c index 6519c0bc6..ef3ac3295 100644 --- a/perf/cairo-perf.c +++ b/perf/cairo-perf.c @@ -28,16 +28,22 @@ #include "cairo-perf.h" -int cairo_perf_iterations = 100; -typedef struct _cairo_perf { - const char *name; - cairo_perf_func_t run; +struct _cairo_perf { + unsigned int iterations; + cairo_boilerplate_target_t *target; unsigned int min_size; unsigned int max_size; -} cairo_perf_t; + unsigned int test_number; +}; -cairo_perf_t perfs[]; +typedef struct _cairo_perf_case { + CAIRO_PERF_DECL (*run); + unsigned int min_size; + unsigned int max_size; +} cairo_perf_case_t; + +cairo_perf_case_t perf_cases[]; /* Some targets just aren't that interesting for performance testing, * (not least because many of these surface types use a meta-surface @@ -65,6 +71,8 @@ target_is_measurable (cairo_boilerplate_target_t *target) case CAIRO_SURFACE_TYPE_WIN32: case CAIRO_SURFACE_TYPE_BEOS: case CAIRO_SURFACE_TYPE_DIRECTFB: + case CAIRO_SURFACE_TYPE_NQUARTZ: + case CAIRO_SURFACE_TYPE_OS2: return TRUE; case CAIRO_SURFACE_TYPE_PDF: case CAIRO_SURFACE_TYPE_PS: @@ -131,90 +139,97 @@ _compute_stats (cairo_perf_ticks_t *values, int num_values, stats_t *stats) stats->std_dev = sqrt(sum / num_values) / stats->mean; } +void +cairo_perf_run (cairo_perf_t *perf, + const char *name, + cairo_perf_func_t perf_func) +{ + static cairo_bool_t first_run = TRUE; + + unsigned int i; + unsigned int size; + cairo_surface_t *surface; + cairo_t *cr; + cairo_perf_ticks_t *times; + stats_t stats; + cairo_boilerplate_target_t *target = perf->target; + + times = xmalloc (perf->iterations * sizeof (cairo_perf_ticks_t)); + + for (size = perf->min_size; size <= perf->max_size; size *= 2) { + surface = (target->create_surface) (name, + target->content, + size, size, + CAIRO_BOILERPLATE_MODE_PERF, + &target->closure); + cairo_perf_timer_set_finalize (target->wait_for_rendering, target->closure); + cr = cairo_create (surface); + for (i =0; i < perf->iterations; i++) { + cairo_perf_yield (); + times[i] = (perf_func) (cr, size, size); + } + + qsort (times, perf->iterations, + sizeof (cairo_perf_ticks_t), compare_cairo_perf_ticks); + + /* Assume the slowest 15% are outliers, and ignore */ + _compute_stats (times, .85 * perf->iterations, &stats); + + if (first_run) { + printf ("[ # ] %8s-%-4s %27s %9s %5s %s\n", + "backend", "content", "test-size", "mean ms", + "std dev.", "iterations"); + first_run = FALSE; + } + + printf ("[%3d] %8s-%-4s %25s-%-3d ", + perf->test_number, target->name, + _content_to_string (target->content), + name, size); + + printf ("%#9.3f %#5.2f%% % 5d\n", + (stats.mean * 1000.0) / cairo_perf_ticks_per_second (), + stats.std_dev * 100.0, perf->iterations); + + perf->test_number++; + } +} + int main (int argc, char *argv[]) { - int i, j, k; - cairo_boilerplate_target_t *target; - cairo_perf_t *perf; - cairo_surface_t *surface; - cairo_t *cr; - unsigned int size; - cairo_perf_ticks_t *times; - stats_t stats; + int i, j; + cairo_perf_case_t *perf_case; + cairo_perf_t perf; const char *cairo_test_target = getenv ("CAIRO_TEST_TARGET"); - double ms; - int test_number; if (getenv("CAIRO_PERF_ITERATIONS")) - cairo_perf_iterations = strtol(getenv("CAIRO_PERF_ITERATIONS"), NULL, 0); - - times = xmalloc (cairo_perf_iterations * sizeof (cairo_perf_ticks_t)); + perf.iterations = strtol(getenv("CAIRO_PERF_ITERATIONS"), NULL, 0); + else + perf.iterations = 100; for (i = 0; targets[i].name; i++) { - target = &targets[i]; - if (! target_is_measurable (target)) + perf.target = &targets[i]; + if (! target_is_measurable (perf.target)) continue; - if (cairo_test_target && ! strstr (cairo_test_target, target->name)) + if (cairo_test_target && ! strstr (cairo_test_target, perf.target->name)) continue; - test_number = 0; + perf.test_number = 0; - for (j = 0; perfs[j].name; j++) { - perf = &perfs[j]; - for (size = perf->min_size; size <= perf->max_size; size *= 2) { - surface = (target->create_surface) (perf->name, - target->content, - size, size, - CAIRO_BOILERPLATE_MODE_PERF, - &target->closure); - cairo_perf_timer_set_finalize (target->wait_for_rendering, target->closure); - cr = cairo_create (surface); - for (k =0; k < cairo_perf_iterations; k++) { - cairo_perf_yield (); - times[k] = perf->run (cr, size, size); - } - - qsort (times, cairo_perf_iterations, - sizeof (cairo_perf_ticks_t), compare_cairo_perf_ticks); - - /* Assume the slowest 15% are outliers, and ignore */ - _compute_stats (times, .85 * cairo_perf_iterations, &stats); - - if (i==0 && j==0 && size == perf->min_size) - printf ("[ # ] %8s-%-4s %27s %9s %5s %s\n", - "backend", "content", "test-size", "mean ms", - "std dev.", "iterations"); - - printf ("[%3d] %8s-%-4s %25s-%-3d ", - test_number, target->name, _content_to_string (target->content), - perf->name, size); - - printf ("%#9.3f %#5.2f%% % 5d\n", - (stats.mean * 1000.0) / cairo_perf_ticks_per_second (), - stats.std_dev * 100.0, cairo_perf_iterations); - - test_number++; - } + for (j = 0; perf_cases[j].run; j++) { + perf_case = &perf_cases[j]; + perf.min_size = perf_case->min_size; + perf.max_size = perf_case->max_size; + perf_case->run (&perf); } } return 0; } -cairo_perf_t perfs[] = { - { "paint_over_solid", paint_over_solid, 64, 512 }, - { "paint_over_solid_alpha", paint_over_solid_alpha, 64, 512 }, - { "paint_source_solid", paint_over_solid, 64, 512 }, - { "paint_source_solid_alpha", paint_over_solid_alpha, 64, 512 }, - - { "paint_over_surf_rgb24", paint_over_solid, 64, 512 }, - { "paint_over_surf_argb32", paint_over_solid_alpha, 64, 512 }, - { "paint_source_surf_rgb24", paint_over_solid, 64, 512 }, - { "paint_source_surf_argb32", paint_over_solid_alpha, 64, 512 }, - - { "tessellate-16", tessellate_16, 100, 100}, - { "tessellate-64", tessellate_64, 100, 100}, - { "tessellate-256", tessellate_256, 100, 100}, +cairo_perf_case_t perf_cases[] = { + { paint, 64, 512}, + { tessellate, 100, 100}, { NULL } }; diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h index f22d76273..6b40ae886 100644 --- a/perf/cairo-perf.h +++ b/perf/cairo-perf.h @@ -58,24 +58,23 @@ cairo_perf_ticks_per_second (void); void cairo_perf_yield (void); +/* running a test case */ +typedef struct _cairo_perf cairo_perf_t; + typedef cairo_perf_ticks_t (*cairo_perf_func_t) (cairo_t *cr, int width, int height); -#define CAIRO_PERF_DECL(func) cairo_perf_ticks_t func (cairo_t *cr, int width, int height) +void +cairo_perf_run (cairo_perf_t *perf, + const char *name, + cairo_perf_func_t perf_func); + +#define CAIRO_PERF_DECL(func) void (func) (cairo_perf_t *perf); /* paint.c */ -CAIRO_PERF_DECL (paint_over_solid); -CAIRO_PERF_DECL (paint_over_solid_alpha); -CAIRO_PERF_DECL (paint_source_solid); -CAIRO_PERF_DECL (paint_source_solid_alpha); -CAIRO_PERF_DECL (paint_over_surface_rgb24); -CAIRO_PERF_DECL (paint_over_surface_argb32); -CAIRO_PERF_DECL (paint_source_surface_rgb24); -CAIRO_PERF_DECL (paint_source_surface_argb32); +CAIRO_PERF_DECL (paint); /* tessellate.c */ -CAIRO_PERF_DECL (tessellate_16); -CAIRO_PERF_DECL (tessellate_64); -CAIRO_PERF_DECL (tessellate_256); +CAIRO_PERF_DECL (tessellate); #endif diff --git a/perf/paint.c b/perf/paint.c index 1556383f0..10aacc045 100644 --- a/perf/paint.c +++ b/perf/paint.c @@ -58,7 +58,7 @@ do_paint (cairo_t *cr, int size) * paint with solid color */ -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_over_solid (cairo_t *cr, int width, int height) { cairo_set_source_rgb (cr, 0.2, 0.6, 0.9); @@ -66,7 +66,7 @@ paint_over_solid (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_over_solid_alpha (cairo_t *cr, int width, int height) { cairo_set_source_rgba (cr, 0.2, 0.6, 0.9, 0.7); @@ -74,7 +74,7 @@ paint_over_solid_alpha (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_source_solid (cairo_t *cr, int width, int height) { cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); @@ -83,7 +83,7 @@ paint_source_solid (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_source_solid_alpha (cairo_t *cr, int width, int height) { cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); @@ -96,13 +96,12 @@ paint_source_solid_alpha (cairo_t *cr, int width, int height) * paint with surface */ - int cached_surface_width = 0; int cached_surface_height = 0; cairo_content_t cached_surface_content = 0; cairo_surface_t *cached_surface = NULL; -void +static void ensure_cached_surface (cairo_t *cr, cairo_content_t content, int w, int h) { cairo_surface_t *target_surface = cairo_get_target (cr); @@ -143,7 +142,7 @@ ensure_cached_surface (cairo_t *cr, cairo_content_t content, int w, int h) cairo_destroy (cr2); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_over_surface_rgb24 (cairo_t *cr, int width, int height) { ensure_cached_surface (cr, CAIRO_CONTENT_COLOR, width, height); @@ -153,7 +152,7 @@ paint_over_surface_rgb24 (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_over_surface_argb32 (cairo_t *cr, int width, int height) { ensure_cached_surface (cr, CAIRO_CONTENT_COLOR_ALPHA, width, height); @@ -163,7 +162,7 @@ paint_over_surface_argb32 (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_source_surface_rgb24 (cairo_t *cr, int width, int height) { ensure_cached_surface (cr, CAIRO_CONTENT_COLOR, width, height); @@ -174,7 +173,7 @@ paint_source_surface_rgb24 (cairo_t *cr, int width, int height) return do_paint (cr, width); } -cairo_perf_ticks_t +static cairo_perf_ticks_t paint_source_surface_argb32 (cairo_t *cr, int width, int height) { ensure_cached_surface (cr, CAIRO_CONTENT_COLOR_ALPHA, width, height); @@ -184,3 +183,16 @@ paint_source_surface_argb32 (cairo_t *cr, int width, int height) return do_paint (cr, width); } + +void +paint (cairo_perf_t *perf) +{ + cairo_perf_run (perf, "paint_over_solid", paint_over_solid); + cairo_perf_run (perf, "paint_over_solid_alpha", paint_over_solid_alpha); + cairo_perf_run (perf, "paint_source_solid", paint_source_solid); + cairo_perf_run (perf, "paint_source_solid_alpha", paint_source_solid_alpha); + cairo_perf_run (perf, "paint_over_surf_rgb24", paint_over_surface_rgb24); + cairo_perf_run (perf, "paint_over_surf_argb32", paint_over_surface_argb32); + cairo_perf_run (perf, "paint_source_surf_rgb24", paint_source_surface_rgb24); + cairo_perf_run (perf, "paint_source_surf_argb32", paint_source_surface_argb32); +} diff --git a/perf/tessellate.c b/perf/tessellate.c index c7b64e593..e8f3dd675 100644 --- a/perf/tessellate.c +++ b/perf/tessellate.c @@ -99,7 +99,7 @@ point_t points[300] = { }; static cairo_perf_ticks_t -tessellate (cairo_t *cr, int num_points) +do_tessellate (cairo_t *cr, int num_points) { int i; @@ -122,22 +122,30 @@ tessellate (cairo_t *cr, int num_points) return cairo_perf_timer_elapsed (); } -cairo_perf_ticks_t +static cairo_perf_ticks_t tessellate_16 (cairo_t *cr, int width, int height) { - return tessellate (cr, 16); + return do_tessellate (cr, 16); } -cairo_perf_ticks_t +static cairo_perf_ticks_t tessellate_64 (cairo_t *cr, int width, int height) { - return tessellate (cr, 64); + return do_tessellate (cr, 64); } -cairo_perf_ticks_t +static cairo_perf_ticks_t tessellate_256 (cairo_t *cr, int width, int height) { - return tessellate (cr, 256); + return do_tessellate (cr, 256); +} + +void +tessellate (cairo_perf_t *perf) +{ + cairo_perf_run (perf, "tessellate-16", tessellate_16); + cairo_perf_run (perf, "tessellate-64", tessellate_64); + cairo_perf_run (perf, "tessellate-256", tessellate_256); } #if 0