2 * Copyright © 2006 Red Hat, Inc.
3 * Copyright © 2009 Chris Wilson
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of the
10 * copyright holders not be used in advertising or publicity
11 * pertaining to distribution of the software without specific,
12 * written prior permission. The copyright holders make no
13 * representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied
17 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
22 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
23 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26 * Authors: Carl Worth <cworth@cworth.org>
27 * Chris Wilson <chris@chris-wilson.co.uk>
30 #include "cairo-perf.h"
40 typedef struct _cairo_perf_report_options
{
43 int print_change_bars
;
44 } cairo_perf_report_options_t
;
46 typedef struct _cairo_perf_diff_files_args
{
47 const char **filenames
;
49 cairo_perf_report_options_t options
;
50 } cairo_perf_diff_files_args_t
;
53 test_diff_cmp (const void *a
, const void *b
)
55 const test_diff_t
*a_diff
= a
;
56 const test_diff_t
*b_diff
= b
;
58 /* Reverse sort by magnitude of change so larger changes come
60 if (a_diff
->change
> b_diff
->change
)
63 if (a_diff
->change
< b_diff
->change
)
69 #define CHANGE_BAR_WIDTH 70
71 print_change_bar (double change
, double max_change
, int use_utf
)
73 int units_per_cell
= (int) ceil (max_change
/ CHANGE_BAR_WIDTH
);
74 static char const *ascii_boxes
[8] = {
75 "****","***" ,"***", "**",
78 static char const *utf_boxes
[8] = {
82 char const **boxes
= use_utf
? utf_boxes
: ascii_boxes
;
84 /* For a 1.0x speedup we want a zero-size bar to show "no
88 while (change
> units_per_cell
) {
89 printf ("%s", boxes
[0]);
90 change
-= units_per_cell
;
93 change
/= units_per_cell
;
96 printf ("%s", boxes
[0]);
97 else if (change
> 6.5/8.0)
98 printf ("%s", boxes
[1]);
99 else if (change
> 5.5/8.0)
100 printf ("%s", boxes
[2]);
101 else if (change
> 4.5/8.0)
102 printf ("%s", boxes
[3]);
103 else if (change
> 3.5/8.0)
104 printf ("%s", boxes
[4]);
105 else if (change
> 2.5/8.0)
106 printf ("%s", boxes
[5]);
107 else if (change
> 1.5/8.0)
108 printf ("%s", boxes
[6]);
109 else if (change
> 0.5/8.0)
110 printf ("%s", boxes
[7]);
116 test_diff_print (test_diff_t
*diff
,
118 cairo_perf_report_options_t
*options
)
124 if (diff
->tests
[0]->size
!= 0) {
125 printf ("(%s, size: %d)\n",
126 diff
->tests
[0]->name
,
127 diff
->tests
[0]->size
);
129 printf ("(%s)\n", diff
->tests
[0]->name
);
132 for (i
= 0; i
< diff
->num_tests
; i
++) {
133 test_time
= diff
->tests
[i
]->stats
.min_ticks
;
134 test_time
/= diff
->tests
[i
]->stats
.ticks_per_ms
;
135 change
= diff
->max
/ test_time
;
136 printf ("%8s-%s-%s\t%6.2f: %5.2fx ",
137 diff
->tests
[i
]->backend
,
138 diff
->tests
[i
]->content
,
139 diff
->tests
[i
]->configuration
,
140 diff
->tests
[i
]->stats
.min_ticks
/ diff
->tests
[i
]->stats
.ticks_per_ms
,
143 if (options
->print_change_bars
)
144 print_change_bar (change
, max_change
, options
->use_utf
);
150 #define MAX(a,b) ((a) > (b) ? (a) : (b))
152 cairo_perf_reports_compare (cairo_perf_report_t
*reports
,
154 cairo_perf_report_options_t
*options
)
157 test_report_t
**tests
, *min_test
;
158 test_diff_t
*diff
, *diffs
;
159 int num_diffs
, max_diffs
;
164 tests
= xmalloc (num_reports
* sizeof (test_report_t
*));
166 max_diffs
= reports
[0].tests_count
;
167 for (i
= 0; i
< num_reports
; i
++) {
168 tests
[i
] = reports
[i
].tests
;
169 if (reports
[i
].tests_count
> max_diffs
)
170 max_diffs
= reports
[i
].tests_count
;
173 diff
= diffs
= xmalloc (max_diffs
* sizeof (test_diff_t
));
179 /* We expect iterations values of 0 when multiple raw reports
180 * for the same test have been condensed into the stats of the
181 * first. So we just skip these later reports that have no
184 for (i
= 0; i
< num_reports
; i
++) {
185 while (tests
[i
]->name
&& tests
[i
]->stats
.iterations
== 0)
193 /* Find the minimum of all current tests, (we have to do this
194 * in case some reports don't have a particular test). */
195 for (i
= 0; i
< num_reports
; i
++) {
196 if (tests
[i
]->name
) {
201 for (++i
; i
< num_reports
; i
++) {
202 if (tests
[i
]->name
&& test_report_cmp_name (tests
[i
], min_test
) < 0)
207 for (i
= 0; i
< num_reports
; i
++) {
212 while (test
[n
].name
&&
213 test_report_cmp_name (&test
[n
], min_test
) == 0)
221 /* For each report that has the current test, record it into
222 * the diff structure. */
224 diff
->tests
= xmalloc (num_tests
* sizeof (test_diff_t
));
225 for (i
= 0; i
< num_reports
; i
++) {
226 while (tests
[i
]->name
&&
227 test_report_cmp_name (tests
[i
], min_test
) == 0)
229 test_time
= tests
[i
]->stats
.min_ticks
;
231 test_time
/= tests
[i
]->stats
.ticks_per_ms
;
232 if (diff
->num_tests
== 0) {
233 diff
->min
= test_time
;
234 diff
->max
= test_time
;
236 if (test_time
< diff
->min
)
237 diff
->min
= test_time
;
238 if (test_time
> diff
->max
)
239 diff
->max
= test_time
;
241 diff
->tests
[diff
->num_tests
++] = tests
[i
];
246 diff
->change
= diff
->max
/ diff
->min
;
254 qsort (diffs
, num_diffs
, sizeof (test_diff_t
), test_diff_cmp
);
257 for (i
= 0; i
< num_diffs
; i
++) {
258 if (fabs (diffs
[i
].change
) > max_change
)
259 max_change
= fabs (diffs
[i
].change
);
262 for (i
= 0; i
< num_diffs
; i
++) {
265 /* Discard as uninteresting a change which is less than the
266 * minimum change required, (default may be overriden on
268 if (fabs (diff
->change
) - 1.0 < options
->min_change
)
271 test_diff_print (diff
, max_change
, options
);
274 for (i
= 0; i
< num_diffs
; i
++)
275 free (diffs
[i
].tests
);
282 usage (const char *argv0
)
284 char const *basename
= strrchr(argv0
, '/');
285 basename
= basename
? basename
+1 : argv0
;
287 "Usage: %s [options] file [...]\n\n",
290 "Computes significant performance differences for cairo performance reports.\n"
291 "Each file should be the output of the cairo-perf program (or \"make perf\").\n"
292 "The following options are available:\n"
294 "--no-utf Use ascii stars instead of utf-8 change bars.\n"
295 " Four stars are printed per factor of speedup.\n"
297 "--no-bars Don't display change bars at all.\n\n"
299 "--use-ms Use milliseconds to calculate differences.\n"
300 " (instead of ticks which are hardware dependant)\n"
302 "--min-change threshold[%%]\n"
303 " Suppress all changes below the given threshold.\n"
304 " The default threshold of 0.05 or 5%% ignores any\n"
305 " speedup or slowdown of 1.05 or less. A threshold\n"
306 " of 0 will cause all output to be reported.\n"
314 cairo_perf_diff_files_args_t
*args
)
318 for (i
= 1; i
< argc
; i
++) {
319 if (strcmp (argv
[i
], "--no-utf") == 0) {
320 args
->options
.use_utf
= 0;
322 else if (strcmp (argv
[i
], "--no-bars") == 0) {
323 args
->options
.print_change_bars
= 0;
325 else if (strcmp (argv
[i
], "--min-change") == 0) {
330 args
->options
.min_change
= strtod (argv
[i
], &end
);
333 args
->options
.min_change
/= 100;
340 args
->num_filenames
++;
341 args
->filenames
= xrealloc (args
->filenames
,
342 args
->num_filenames
* sizeof (char *));
343 args
->filenames
[args
->num_filenames
- 1] = argv
[i
];
349 main (int argc
, const char *argv
[])
351 cairo_perf_diff_files_args_t args
= {
352 NULL
, /* filenames */
353 0, /* num_filenames */
355 0.05, /* min change */
357 1, /* display change bars? */
360 cairo_perf_report_t
*reports
;
364 parse_args (argc
, argv
, &args
);
366 if (args
.num_filenames
< 1)
369 reports
= xcalloc (args
.num_filenames
, sizeof (cairo_perf_report_t
));
371 for (i
= 0; i
< args
.num_filenames
; i
++) {
372 cairo_perf_report_load (&reports
[i
], args
.filenames
[i
],
373 test_report_cmp_name
);
374 printf ("loaded: %s, %d tests\n",
375 args
.filenames
[i
], reports
[i
].tests_count
);
378 cairo_perf_reports_compare (reports
, args
.num_filenames
, &args
.options
);
380 /* Pointless memory cleanup, (would be a great place for talloc) */
381 free (args
.filenames
);
382 for (i
= 0; i
< args
.num_filenames
; i
++) {
383 for (t
= reports
[i
].tests
; t
->name
; t
++) {
388 free (reports
[i
].tests
);
389 free (reports
[i
].configuration
);