1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
3 * Copyright © 2006 Mozilla Corporation
4 * Copyright © 2006 Red Hat, Inc.
6 * Permission to use, copy, modify, distribute, and sell this software
7 * and its documentation for any purpose is hereby granted without
8 * fee, provided that the above copyright notice appear in all copies
9 * and that both that copyright notice and this permission notice
10 * appear in supporting documentation, and that the name of
11 * the authors not be used in advertising or publicity pertaining to
12 * distribution of the software without specific, written prior
13 * permission. The authors make no representations about the
14 * suitability of this software for any purpose. It is provided "as
15 * is" without express or implied warranty.
17 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
20 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
23 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * Authors: Vladimir Vukicevic <vladimir@pobox.com>
26 * Carl Worth <cworth@cworth.org>
29 #include "cairo-perf.h"
31 #include "cairo-boilerplate-getopt.h"
39 #include <fontconfig/fontconfig.h>
46 #define CAIRO_PERF_ITERATIONS_DEFAULT 100
47 #define CAIRO_PERF_LOW_STD_DEV 0.03
48 #define CAIRO_PERF_STABLE_STD_DEV_COUNT 5
50 typedef struct _cairo_perf_case
{
51 CAIRO_PERF_DECL (*run
);
52 unsigned int min_size
;
53 unsigned int max_size
;
56 cairo_perf_case_t perf_cases
[];
58 /* Some targets just aren't that interesting for performance testing,
59 * (not least because many of these surface types use a meta-surface
60 * and as such defer the "real" rendering to later, so our timing
61 * loops wouldn't count the real work, just the recording by the
64 target_is_measurable (cairo_boilerplate_target_t
*target
)
66 switch (target
->expected_type
) {
67 case CAIRO_SURFACE_TYPE_IMAGE
:
68 if (strcmp (target
->name
, "pdf") == 0 ||
69 strcmp (target
->name
, "ps") == 0)
77 case CAIRO_SURFACE_TYPE_XLIB
:
78 if (strcmp (target
->name
, "xlib-fallback") == 0)
86 case CAIRO_SURFACE_TYPE_XCB
:
87 case CAIRO_SURFACE_TYPE_GLITZ
:
88 case CAIRO_SURFACE_TYPE_QUARTZ
:
89 case CAIRO_SURFACE_TYPE_WIN32
:
90 case CAIRO_SURFACE_TYPE_BEOS
:
91 case CAIRO_SURFACE_TYPE_DIRECTFB
:
92 #if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 2)
93 case CAIRO_SURFACE_TYPE_OS2
:
96 case CAIRO_SURFACE_TYPE_PDF
:
97 case CAIRO_SURFACE_TYPE_PS
:
98 case CAIRO_SURFACE_TYPE_SVG
:
105 _content_to_string (cairo_content_t content
, cairo_bool_t similar
)
107 switch (content
|similar
) {
108 case CAIRO_CONTENT_COLOR
:
110 case CAIRO_CONTENT_COLOR
|1:
112 case CAIRO_CONTENT_ALPHA
:
114 case CAIRO_CONTENT_ALPHA
|1:
116 case CAIRO_CONTENT_COLOR_ALPHA
:
118 case CAIRO_CONTENT_COLOR_ALPHA
|1:
121 return "<unknown_content>";
126 cairo_perf_has_similar (cairo_perf_t
*perf
)
128 cairo_surface_t
*target
= cairo_get_target (perf
->cr
);
130 /* exclude the image backend */
131 if (cairo_surface_get_type (target
) == CAIRO_SURFACE_TYPE_IMAGE
)
138 cairo_perf_run (cairo_perf_t
*perf
,
140 cairo_perf_func_t perf_func
)
142 static cairo_bool_t first_run
= TRUE
;
143 unsigned int i
, similar
, has_similar
;
144 cairo_perf_ticks_t
*times
;
145 cairo_stats_t stats
= {0.0, 0.0};
146 int low_std_dev_count
;
148 if (perf
->num_names
) {
149 for (i
= 0; i
< perf
->num_names
; i
++)
150 if (strstr (name
, perf
->names
[i
]))
156 if (perf
->list_only
) {
157 printf ("%s\n", name
);
163 printf ("[ # ] %s-%-s %s %s %s ...\n",
164 "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
166 printf ("[ # ] %8s-%-4s %28s %8s %8s %5s %5s %s\n",
167 "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
168 "stddev.", "iterations");
172 times
= xmalloc (perf
->iterations
* sizeof (cairo_perf_ticks_t
));
174 has_similar
= cairo_perf_has_similar (perf
);
175 for (similar
= 0; similar
<= has_similar
; similar
++) {
176 /* We run one iteration in advance to warm caches, etc. */
179 cairo_push_group_with_content (perf
->cr
,
180 cairo_boilerplate_content (perf
->target
->content
));
181 (perf_func
) (perf
->cr
, perf
->size
, perf
->size
);
183 cairo_pattern_destroy (cairo_pop_group (perf
->cr
));
185 low_std_dev_count
= 0;
186 for (i
=0; i
< perf
->iterations
; i
++) {
189 cairo_push_group_with_content (perf
->cr
,
190 cairo_boilerplate_content (perf
->target
->content
));
191 times
[i
] = (perf_func
) (perf
->cr
, perf
->size
, perf
->size
);
193 cairo_pattern_destroy (cairo_pop_group (perf
->cr
));
197 printf ("[*] %s-%s %s-%d %g",
199 _content_to_string (perf
->target
->content
, similar
),
201 cairo_perf_ticks_per_second () / 1000.0);
202 printf (" %lld", times
[i
]);
203 } else if (! perf
->exact_iterations
) {
205 _cairo_stats_compute (&stats
, times
, i
+1);
207 if (stats
.std_dev
<= CAIRO_PERF_LOW_STD_DEV
)
210 if (low_std_dev_count
>= CAIRO_PERF_STABLE_STD_DEV_COUNT
)
213 low_std_dev_count
= 0;
222 _cairo_stats_compute (&stats
, times
, i
);
223 printf ("[%3d] %8s-%-5s %26s-%-3d ",
224 perf
->test_number
, perf
->target
->name
,
225 _content_to_string (perf
->target
->content
, similar
),
228 printf ("%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
230 (stats
.min_ticks
* 1000.0) / cairo_perf_ticks_per_second (),
231 (stats
.median_ticks
* 1000.0) / cairo_perf_ticks_per_second (),
232 stats
.std_dev
* 100.0, stats
.iterations
);
241 usage (const char *argv0
)
244 "Usage: %s [-l] [-r] [-i iterations] [test-names ...]\n"
247 "Run the cairo performance test suite over the given tests (all by default)\n"
248 "The command-line arguments are interpreted as follows:\n"
250 " -r raw; display each time measurement instead of summary statistics\n"
251 " -i iterations; specify the number of iterations per test case\n"
252 " -l list only; just list selected test case names without executing\n"
254 "If test names are given they are used as sub-string matches so a command\n"
255 "such as \"cairo-perf text\" can be used to run all text test cases.\n",
260 parse_options (cairo_perf_t
*perf
, int argc
, char *argv
[])
266 if ((iters
= getenv("CAIRO_PERF_ITERATIONS")) && *iters
)
267 perf
->iterations
= strtol(iters
, NULL
, 0);
269 perf
->iterations
= CAIRO_PERF_ITERATIONS_DEFAULT
;
270 perf
->exact_iterations
= 0;
273 perf
->list_only
= FALSE
;
278 c
= _cairo_getopt (argc
, argv
, "i:lr");
284 perf
->exact_iterations
= TRUE
;
285 perf
->iterations
= strtoul (optarg
, &end
, 10);
287 fprintf (stderr
, "Invalid argument for -i (not an integer): %s\n",
293 perf
->list_only
= TRUE
;
299 fprintf (stderr
, "Internal error: unhandled option: %c\n", c
);
307 perf
->names
= &argv
[optind
];
308 perf
->num_names
= argc
- optind
;
313 check_cpu_affinity(void)
315 #ifdef HAVE_SCHED_GETAFFINITY
320 if (sched_getaffinity(0, sizeof(affinity
), &affinity
)) {
321 perror("sched_getaffinity");
325 for(i
= 0, cpu_count
= 0; i
< CPU_SETSIZE
; ++i
) {
326 if (CPU_ISSET(i
, &affinity
))
332 "WARNING: cairo-perf has not been bound to a single CPU.\n",
340 "WARNING: Cannot check CPU affinity for this platform.\n",
347 cairo_perf_fini (void)
349 cairo_debug_reset_static_data ();
357 main (int argc
, char *argv
[])
359 int i
, j
, num_targets
;
360 cairo_perf_case_t
*perf_case
;
362 cairo_boilerplate_target_t
**targets
;
363 cairo_surface_t
*surface
;
365 parse_options (&perf
, argc
, argv
);
367 if (check_cpu_affinity()) {
369 "NOTICE: cairo-perf and the X server should be bound to CPUs (either the same\n"
370 "or separate) on SMP systems. Not doing so causes random results when the X\n"
371 "server is moved to or from cairo-perf's CPU during the benchmarks:\n"
373 " $ sudo taskset -cp 0 $(pidof X)\n"
374 " $ taskset -cp 1 $$\n"
376 "See taskset(1) for information about changing CPU affinity.\n",
380 targets
= cairo_boilerplate_get_targets (&num_targets
, NULL
);
382 for (i
= 0; i
< num_targets
; i
++) {
383 cairo_boilerplate_target_t
*target
= targets
[i
];
385 if (! target_is_measurable (target
))
388 perf
.target
= target
;
389 perf
.test_number
= 0;
391 for (j
= 0; perf_cases
[j
].run
; j
++) {
393 perf_case
= &perf_cases
[j
];
395 for (perf
.size
= perf_case
->min_size
;
396 perf
.size
<= perf_case
->max_size
;
399 surface
= (target
->create_surface
) (NULL
,
401 perf
.size
, perf
.size
,
402 CAIRO_BOILERPLATE_MODE_PERF
,
404 if (surface
== NULL
) {
406 "Error: Failed to create target surface: %s\n",
408 cairo_boilerplate_free_targets (targets
);
413 cairo_perf_timer_set_synchronize (target
->synchronize
,
416 perf
.cr
= cairo_create (surface
);
418 perf_case
->run (&perf
, perf
.cr
, perf
.size
, perf
.size
);
420 if (cairo_status (perf
.cr
)) {
421 fprintf (stderr
, "Error: Test left cairo in an error state: %s\n",
422 cairo_status_to_string (cairo_status (perf
.cr
)));
423 cairo_boilerplate_free_targets (targets
);
428 cairo_destroy (perf
.cr
);
429 cairo_surface_destroy (surface
);
432 target
->cleanup (target
->closure
);
437 cairo_boilerplate_free_targets (targets
);
443 cairo_perf_case_t perf_cases
[] = {
445 { paint_with_alpha
, 256, 512},
449 { tessellate
, 100, 100},
450 { subimage_copy
, 16, 512},
451 { pattern_create_radial
, 16, 16},
453 { world_map
, 800, 800},
454 { box_outline
, 100, 100},
455 { mosaic
, 800, 800 },
456 { long_lines
, 100, 100},
457 { unaligned_clip
, 100, 100},
458 { rectangles
, 512, 512},
459 { long_dashed_lines
, 512, 512},