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 #define _GNU_SOURCE 1 /* for sched_getaffinity() */
31 #include "cairo-perf.h"
32 #include "cairo-stats.h"
34 #include "cairo-boilerplate-getopt.h"
42 #include <fontconfig/fontconfig.h>
49 #define CAIRO_PERF_ITERATIONS_DEFAULT 100
50 #define CAIRO_PERF_LOW_STD_DEV 0.03
51 #define CAIRO_PERF_STABLE_STD_DEV_COUNT 5
53 typedef struct _cairo_perf_case
{
54 CAIRO_PERF_DECL (*run
);
55 unsigned int min_size
;
56 unsigned int max_size
;
59 const cairo_perf_case_t perf_cases
[];
61 /* Some targets just aren't that interesting for performance testing,
62 * (not least because many of these surface types use a meta-surface
63 * and as such defer the "real" rendering to later, so our timing
64 * loops wouldn't count the real work, just the recording by the
67 target_is_measurable (cairo_boilerplate_target_t
*target
)
69 switch (target
->expected_type
) {
70 case CAIRO_SURFACE_TYPE_IMAGE
:
71 if (strcmp (target
->name
, "pdf") == 0 ||
72 strcmp (target
->name
, "ps") == 0)
80 case CAIRO_SURFACE_TYPE_XLIB
:
81 if (strcmp (target
->name
, "xlib-fallback") == 0)
89 case CAIRO_SURFACE_TYPE_XCB
:
90 case CAIRO_SURFACE_TYPE_GLITZ
:
91 case CAIRO_SURFACE_TYPE_QUARTZ
:
92 case CAIRO_SURFACE_TYPE_WIN32
:
93 case CAIRO_SURFACE_TYPE_BEOS
:
94 case CAIRO_SURFACE_TYPE_DIRECTFB
:
95 #if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 2)
96 case CAIRO_SURFACE_TYPE_OS2
:
99 case CAIRO_SURFACE_TYPE_PDF
:
100 case CAIRO_SURFACE_TYPE_PS
:
101 case CAIRO_SURFACE_TYPE_SVG
:
108 _content_to_string (cairo_content_t content
, cairo_bool_t similar
)
110 switch (content
|similar
) {
111 case CAIRO_CONTENT_COLOR
:
113 case CAIRO_CONTENT_COLOR
|1:
115 case CAIRO_CONTENT_ALPHA
:
117 case CAIRO_CONTENT_ALPHA
|1:
119 case CAIRO_CONTENT_COLOR_ALPHA
:
121 case CAIRO_CONTENT_COLOR_ALPHA
|1:
124 return "<unknown_content>";
129 cairo_perf_has_similar (cairo_perf_t
*perf
)
131 cairo_surface_t
*target
= cairo_get_target (perf
->cr
);
133 /* exclude the image backend */
134 if (cairo_surface_get_type (target
) == CAIRO_SURFACE_TYPE_IMAGE
)
141 cairo_perf_run (cairo_perf_t
*perf
,
143 cairo_perf_func_t perf_func
)
145 static cairo_bool_t first_run
= TRUE
;
146 unsigned int i
, similar
, has_similar
;
147 cairo_perf_ticks_t
*times
;
148 cairo_stats_t stats
= {0.0, 0.0};
149 int low_std_dev_count
;
151 if (perf
->num_names
) {
152 for (i
= 0; i
< perf
->num_names
; i
++)
153 if (strstr (name
, perf
->names
[i
]))
159 if (perf
->list_only
) {
160 printf ("%s\n", name
);
166 printf ("[ # ] %s-%-s %s %s %s ...\n",
167 "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
169 printf ("[ # ] %8s-%-4s %28s %8s %8s %5s %5s %s\n",
170 "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
171 "stddev.", "iterations");
177 has_similar
= cairo_perf_has_similar (perf
);
178 for (similar
= 0; similar
<= has_similar
; similar
++) {
179 /* We run one iteration in advance to warm caches, etc. */
182 cairo_push_group_with_content (perf
->cr
,
183 cairo_boilerplate_content (perf
->target
->content
));
184 (perf_func
) (perf
->cr
, perf
->size
, perf
->size
);
186 cairo_pattern_destroy (cairo_pop_group (perf
->cr
));
188 low_std_dev_count
= 0;
189 for (i
=0; i
< perf
->iterations
; i
++) {
192 cairo_push_group_with_content (perf
->cr
,
193 cairo_boilerplate_content (perf
->target
->content
));
194 times
[i
] = (perf_func
) (perf
->cr
, perf
->size
, perf
->size
);
196 cairo_pattern_destroy (cairo_pop_group (perf
->cr
));
200 printf ("[*] %s-%s %s-%d %g",
202 _content_to_string (perf
->target
->content
, similar
),
204 cairo_perf_ticks_per_second () / 1000.0);
205 printf (" %lld", (long long) times
[i
]);
206 } else if (! perf
->exact_iterations
) {
208 _cairo_stats_compute (&stats
, times
, i
+1);
210 if (stats
.std_dev
<= CAIRO_PERF_LOW_STD_DEV
)
213 if (low_std_dev_count
>= CAIRO_PERF_STABLE_STD_DEV_COUNT
)
216 low_std_dev_count
= 0;
225 _cairo_stats_compute (&stats
, times
, i
);
226 printf ("[%3d] %8s-%-5s %26s-%-3d ",
227 perf
->test_number
, perf
->target
->name
,
228 _content_to_string (perf
->target
->content
, similar
),
231 printf ("%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
232 (long long) stats
.min_ticks
,
233 (stats
.min_ticks
* 1000.0) / cairo_perf_ticks_per_second (),
234 (stats
.median_ticks
* 1000.0) / cairo_perf_ticks_per_second (),
235 stats
.std_dev
* 100.0, stats
.iterations
);
243 usage (const char *argv0
)
246 "Usage: %s [-l] [-r] [-i iterations] [test-names ...]\n"
249 "Run the cairo performance test suite over the given tests (all by default)\n"
250 "The command-line arguments are interpreted as follows:\n"
252 " -r raw; display each time measurement instead of summary statistics\n"
253 " -i iterations; specify the number of iterations per test case\n"
254 " -l list only; just list selected test case names without executing\n"
256 "If test names are given they are used as sub-string matches so a command\n"
257 "such as \"cairo-perf text\" can be used to run all text test cases.\n",
262 parse_options (cairo_perf_t
*perf
, int argc
, char *argv
[])
268 if ((iters
= getenv("CAIRO_PERF_ITERATIONS")) && *iters
)
269 perf
->iterations
= strtol(iters
, NULL
, 0);
271 perf
->iterations
= CAIRO_PERF_ITERATIONS_DEFAULT
;
272 perf
->exact_iterations
= 0;
275 perf
->list_only
= FALSE
;
280 c
= _cairo_getopt (argc
, argv
, "i:lr");
286 perf
->exact_iterations
= TRUE
;
287 perf
->iterations
= strtoul (optarg
, &end
, 10);
289 fprintf (stderr
, "Invalid argument for -i (not an integer): %s\n",
295 perf
->list_only
= TRUE
;
301 fprintf (stderr
, "Internal error: unhandled option: %c\n", c
);
309 perf
->names
= &argv
[optind
];
310 perf
->num_names
= argc
- optind
;
315 check_cpu_affinity(void)
317 #ifdef HAVE_SCHED_GETAFFINITY
322 if (sched_getaffinity(0, sizeof(affinity
), &affinity
)) {
323 perror("sched_getaffinity");
327 for(i
= 0, cpu_count
= 0; i
< CPU_SETSIZE
; ++i
) {
328 if (CPU_ISSET(i
, &affinity
))
334 "WARNING: cairo-perf has not been bound to a single CPU.\n",
342 "WARNING: Cannot check CPU affinity for this platform.\n",
349 cairo_perf_fini (cairo_perf_t
*perf
)
351 cairo_boilerplate_free_targets (perf
->targets
);
353 cairo_debug_reset_static_data ();
361 main (int argc
, char *argv
[])
365 cairo_surface_t
*surface
;
367 parse_options (&perf
, argc
, argv
);
369 if (check_cpu_affinity()) {
371 "NOTICE: cairo-perf and the X server should be bound to CPUs (either the same\n"
372 "or separate) on SMP systems. Not doing so causes random results when the X\n"
373 "server is moved to or from cairo-perf's CPU during the benchmarks:\n"
375 " $ sudo taskset -cp 0 $(pidof X)\n"
376 " $ taskset -cp 1 $$\n"
378 "See taskset(1) for information about changing CPU affinity.\n",
382 perf
.targets
= cairo_boilerplate_get_targets (&perf
.num_targets
, NULL
);
383 perf
.times
= xmalloc (perf
.iterations
* sizeof (cairo_perf_ticks_t
));
385 for (i
= 0; i
< perf
.num_targets
; i
++) {
386 cairo_boilerplate_target_t
*target
= perf
.targets
[i
];
388 if (! target_is_measurable (target
))
391 perf
.target
= target
;
392 perf
.test_number
= 0;
394 for (j
= 0; perf_cases
[j
].run
; j
++) {
395 const cairo_perf_case_t
*perf_case
= &perf_cases
[j
];
397 for (perf
.size
= perf_case
->min_size
;
398 perf
.size
<= perf_case
->max_size
;
403 surface
= (target
->create_surface
) (NULL
,
405 perf
.size
, perf
.size
,
406 perf
.size
, perf
.size
,
407 CAIRO_BOILERPLATE_MODE_PERF
,
410 if (surface
== NULL
) {
412 "Error: Failed to create target surface: %s\n",
417 cairo_perf_timer_set_synchronize (target
->synchronize
, closure
);
419 perf
.cr
= cairo_create (surface
);
421 perf_case
->run (&perf
, perf
.cr
, perf
.size
, perf
.size
);
423 if (cairo_status (perf
.cr
)) {
424 fprintf (stderr
, "Error: Test left cairo in an error state: %s\n",
425 cairo_status_to_string (cairo_status (perf
.cr
)));
428 cairo_destroy (perf
.cr
);
429 cairo_surface_destroy (surface
);
432 target
->cleanup (closure
);
437 cairo_perf_fini (&perf
);
442 const cairo_perf_case_t perf_cases
[] = {
444 { paint_with_alpha
, 256, 512},
448 { tessellate
, 100, 100},
449 { subimage_copy
, 16, 512},
450 { pattern_create_radial
, 16, 16},
452 { world_map
, 800, 800},
453 { box_outline
, 100, 100},
454 { mosaic
, 800, 800 },
455 { long_lines
, 100, 100},
456 { unaligned_clip
, 100, 100},
457 { rectangles
, 512, 512},
458 { rounded_rectangles
, 512, 512},
459 { long_dashed_lines
, 512, 512},
460 { composite_checker
, 16, 512},