mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-06-02 20:28:18 +02:00
perf: Rework the suite to allow multiple performance tests to be defined in one file.
This commit is contained in:
parent
cd6b44e9c9
commit
3c19a6413c
5 changed files with 177 additions and 127 deletions
66
perf/README
66
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!
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
32
perf/paint.c
32
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue