perf: Rework the suite to allow multiple performance tests to be defined in one file.

This commit is contained in:
Carl Worth 2006-09-29 16:42:44 -07:00
parent cd6b44e9c9
commit 3c19a6413c
5 changed files with 177 additions and 127 deletions

View file

@ -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!

View file

@ -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 }
};

View file

@ -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

View file

@ -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);
}

View file

@ -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