[configure.in] Build fails on Solaris due to non-POSIX ctime_r()
[cairo/haiku.git] / perf / cairo-perf.c
blobbfc0240fee430457df1fa5197b1b145f6813e87d
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /*
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"
33 /* For basename */
34 #ifdef HAVE_LIBGEN_H
35 #include <libgen.h>
36 #endif
38 #if HAVE_FCFINI
39 #include <fontconfig/fontconfig.h>
40 #endif
42 #ifdef HAVE_SCHED_H
43 #include <sched.h>
44 #endif
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;
54 } cairo_perf_case_t;
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
62 * meta-surface. */
63 static cairo_bool_t
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)
71 return FALSE;
73 else
75 return TRUE;
77 case CAIRO_SURFACE_TYPE_XLIB:
78 if (strcmp (target->name, "xlib-fallback") == 0)
80 return FALSE;
82 else
84 return TRUE;
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:
94 #endif
95 return TRUE;
96 case CAIRO_SURFACE_TYPE_PDF:
97 case CAIRO_SURFACE_TYPE_PS:
98 case CAIRO_SURFACE_TYPE_SVG:
99 default:
100 return FALSE;
104 static const char *
105 _content_to_string (cairo_content_t content, cairo_bool_t similar)
107 switch (content|similar) {
108 case CAIRO_CONTENT_COLOR:
109 return "rgb";
110 case CAIRO_CONTENT_COLOR|1:
111 return "rgb&";
112 case CAIRO_CONTENT_ALPHA:
113 return "a";
114 case CAIRO_CONTENT_ALPHA|1:
115 return "a&";
116 case CAIRO_CONTENT_COLOR_ALPHA:
117 return "rgba";
118 case CAIRO_CONTENT_COLOR_ALPHA|1:
119 return "rgba&";
120 default:
121 return "<unknown_content>";
125 static cairo_bool_t
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)
132 return FALSE;
134 return TRUE;
137 void
138 cairo_perf_run (cairo_perf_t *perf,
139 const char *name,
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]))
151 goto NAME_FOUND;
152 return;
154 NAME_FOUND:
156 if (perf->list_only) {
157 printf ("%s\n", name);
158 return;
161 if (first_run) {
162 if (perf->raw)
163 printf ("[ # ] %s-%-s %s %s %s ...\n",
164 "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
165 else
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");
169 first_run = FALSE;
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. */
177 cairo_perf_yield ();
178 if (similar)
179 cairo_push_group_with_content (perf->cr,
180 cairo_boilerplate_content (perf->target->content));
181 (perf_func) (perf->cr, perf->size, perf->size);
182 if (similar)
183 cairo_pattern_destroy (cairo_pop_group (perf->cr));
185 low_std_dev_count = 0;
186 for (i =0; i < perf->iterations; i++) {
187 cairo_perf_yield ();
188 if (similar)
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);
192 if (similar)
193 cairo_pattern_destroy (cairo_pop_group (perf->cr));
195 if (perf->raw) {
196 if (i == 0)
197 printf ("[*] %s-%s %s-%d %g",
198 perf->target->name,
199 _content_to_string (perf->target->content, similar),
200 name, perf->size,
201 cairo_perf_ticks_per_second () / 1000.0);
202 printf (" %lld", times[i]);
203 } else if (! perf->exact_iterations) {
204 if (i > 0) {
205 _cairo_stats_compute (&stats, times, i+1);
207 if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV)
209 low_std_dev_count++;
210 if (low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
211 break;
212 } else {
213 low_std_dev_count = 0;
219 if (perf->raw) {
220 printf ("\n");
221 } else {
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),
226 name, perf->size);
228 printf ("%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
229 stats.min_ticks,
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);
235 perf->test_number++;
237 free (times);
240 static void
241 usage (const char *argv0)
243 fprintf (stderr,
244 "Usage: %s [-l] [-r] [-i iterations] [test-names ...]\n"
245 " %s -l\n"
246 "\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"
249 "\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"
253 "\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",
256 argv0, argv0);
259 static void
260 parse_options (cairo_perf_t *perf, int argc, char *argv[])
262 int c;
263 const char *iters;
264 char *end;
266 if ((iters = getenv("CAIRO_PERF_ITERATIONS")) && *iters)
267 perf->iterations = strtol(iters, NULL, 0);
268 else
269 perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
270 perf->exact_iterations = 0;
272 perf->raw = FALSE;
273 perf->list_only = FALSE;
274 perf->names = NULL;
275 perf->num_names = 0;
277 while (1) {
278 c = _cairo_getopt (argc, argv, "i:lr");
279 if (c == -1)
280 break;
282 switch (c) {
283 case 'i':
284 perf->exact_iterations = TRUE;
285 perf->iterations = strtoul (optarg, &end, 10);
286 if (*end != '\0') {
287 fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
288 optarg);
289 exit (1);
291 break;
292 case 'l':
293 perf->list_only = TRUE;
294 break;
295 case 'r':
296 perf->raw = TRUE;
297 break;
298 default:
299 fprintf (stderr, "Internal error: unhandled option: %c\n", c);
300 /* fall-through */
301 case '?':
302 usage (argv[0]);
303 exit (1);
306 if (optind < argc) {
307 perf->names = &argv[optind];
308 perf->num_names = argc - optind;
312 static int
313 check_cpu_affinity(void)
315 #ifdef HAVE_SCHED_GETAFFINITY
317 cpu_set_t affinity;
318 int i, cpu_count;
320 if (sched_getaffinity(0, sizeof(affinity), &affinity)) {
321 perror("sched_getaffinity");
322 return -1;
325 for(i = 0, cpu_count = 0; i < CPU_SETSIZE; ++i) {
326 if (CPU_ISSET(i, &affinity))
327 ++cpu_count;
330 if (cpu_count > 1) {
331 fputs(
332 "WARNING: cairo-perf has not been bound to a single CPU.\n",
333 stderr);
334 return -1;
337 return 0;
338 #else
339 fputs(
340 "WARNING: Cannot check CPU affinity for this platform.\n",
341 stderr);
342 return -1;
343 #endif
346 static void
347 cairo_perf_fini (void)
349 cairo_debug_reset_static_data ();
350 #if HAVE_FCFINI
351 FcFini ();
352 #endif
357 main (int argc, char *argv[])
359 int i, j, num_targets;
360 cairo_perf_case_t *perf_case;
361 cairo_perf_t perf;
362 cairo_boilerplate_target_t **targets;
363 cairo_surface_t *surface;
365 parse_options (&perf, argc, argv);
367 if (check_cpu_affinity()) {
368 fputs(
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"
372 "\n"
373 " $ sudo taskset -cp 0 $(pidof X)\n"
374 " $ taskset -cp 1 $$\n"
375 "\n"
376 "See taskset(1) for information about changing CPU affinity.\n",
377 stderr);
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))
386 continue;
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;
397 perf.size *= 2)
399 surface = (target->create_surface) (NULL,
400 target->content,
401 perf.size, perf.size,
402 CAIRO_BOILERPLATE_MODE_PERF,
403 &target->closure);
404 if (surface == NULL) {
405 fprintf (stderr,
406 "Error: Failed to create target surface: %s\n",
407 target->name);
408 cairo_boilerplate_free_targets (targets);
409 cairo_perf_fini ();
410 exit (1);
413 cairo_perf_timer_set_synchronize (target->synchronize,
414 target->closure);
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);
424 cairo_perf_fini ();
425 exit (1);
428 cairo_destroy (perf.cr);
429 cairo_surface_destroy (surface);
431 if (target->cleanup)
432 target->cleanup (target->closure);
437 cairo_boilerplate_free_targets (targets);
438 cairo_perf_fini ();
440 return 0;
443 cairo_perf_case_t perf_cases[] = {
444 { paint, 256, 512},
445 { paint_with_alpha, 256, 512},
446 { fill, 64, 256},
447 { stroke, 64, 256},
448 { text, 64, 256},
449 { tessellate, 100, 100},
450 { subimage_copy, 16, 512},
451 { pattern_create_radial, 16, 16},
452 { zrusin, 415, 415},
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},
460 { NULL }