diff options
Diffstat (limited to 'libs/cairo-1.16.0/perf/cairo-perf-diff-files.c')
-rw-r--r-- | libs/cairo-1.16.0/perf/cairo-perf-diff-files.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/libs/cairo-1.16.0/perf/cairo-perf-diff-files.c b/libs/cairo-1.16.0/perf/cairo-perf-diff-files.c new file mode 100644 index 0000000..4d3ff7a --- /dev/null +++ b/libs/cairo-1.16.0/perf/cairo-perf-diff-files.c @@ -0,0 +1,506 @@ +/* + * Copyright © 2006 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of the + * copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * Authors: Carl Worth <cworth@cworth.org> + */ + +#include "cairo-perf.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <math.h> +#include <assert.h> + +typedef struct _cairo_perf_report_options { + double min_change; + int use_utf; + int print_change_bars; + int use_ticks; +} cairo_perf_report_options_t; + +typedef struct _cairo_perf_diff_files_args { + const char **filenames; + int num_filenames; + cairo_perf_report_options_t options; +} cairo_perf_diff_files_args_t; + +static int +test_diff_cmp_speedup_before_slowdown (const void *a, + const void *b) +{ + const test_diff_t *a_diff = a; + const test_diff_t *b_diff = b; + + /* First make all speedups come before all slowdowns. */ + if (a_diff->change > 0 && b_diff->change < 0) + return -1; + if (a_diff->change < 0 && b_diff->change > 0) + return 1; + + if (a_diff->change == b_diff->change) + return 0; + + /* Large speedups come first. */ + if (a_diff->change > 0) { + if (a_diff->change > b_diff->change) + return -1; + else + return 1; + } + + /* Large slowdowns come last. */ + if (a_diff->change < 0) { + if (a_diff->change < b_diff->change) + return 1; + else + return -1; + } + + return 0; +} + +static int +test_diff_cmp (const void *a, + const void *b) +{ + const test_diff_t *a_diff = a; + const test_diff_t *b_diff = b; + + /* Reverse sort by magnitude of change so larger changes come + * first */ + if (a_diff->change > b_diff->change) + return -1; + + if (a_diff->change < b_diff->change) + return 1; + + return 0; +} + +#define CHANGE_BAR_WIDTH 70 +static void +print_change_bar (double change, + double max_change, + int use_utf) +{ + int units_per_cell = ceil (max_change / CHANGE_BAR_WIDTH); + static char const *ascii_boxes[8] = { + "****","***" ,"***", "**", + "**", "*", "*", "" + }; + static char const *utf_boxes[8] = { + "█", "▉", "▊", "▋", + "▌", "▍", "▎", "▏" + }; + char const **boxes = use_utf ? utf_boxes : ascii_boxes; + + /* For a 1.0x speedup we want a zero-size bar to show "no + * change". */ + change -= 1.0; + + while (change > units_per_cell) { + printf ("%s", boxes[0]); + change -= units_per_cell; + } + + change /= units_per_cell; + + if (change > 7.5/8.0) + printf ("%s", boxes[0]); + else if (change > 6.5/8.0) + printf ("%s", boxes[1]); + else if (change > 5.5/8.0) + printf ("%s", boxes[2]); + else if (change > 4.5/8.0) + printf ("%s", boxes[3]); + else if (change > 3.5/8.0) + printf ("%s", boxes[4]); + else if (change > 2.5/8.0) + printf ("%s", boxes[5]); + else if (change > 1.5/8.0) + printf ("%s", boxes[6]); + else if (change > 0.5/8.0) + printf ("%s", boxes[7]); + + printf ("\n"); +} + +static void +test_diff_print_binary (test_diff_t *diff, + double max_change, + cairo_perf_report_options_t *options) +{ + if (diff->tests[0]->size) + printf ("%5s-%-4s %26s-%-3d", + diff->tests[0]->backend, diff->tests[0]->content, + diff->tests[0]->name, diff->tests[0]->size); + else + printf ("%5s %26s", diff->tests[0]->backend, diff->tests[0]->name); + + printf (" %6.2f (%.2f %4.2f%%) -> %6.2f (%.2f %4.2f%%): %5.2fx ", + diff->tests[0]->stats.min_ticks / diff->tests[0]->stats.ticks_per_ms, + diff->tests[0]->stats.median_ticks / diff->tests[0]->stats.ticks_per_ms, + diff->tests[0]->stats.std_dev * 100, + diff->tests[1]->stats.min_ticks / diff->tests[1]->stats.ticks_per_ms, + diff->tests[1]->stats.median_ticks / diff->tests[1]->stats.ticks_per_ms, + diff->tests[1]->stats.std_dev * 100, + fabs (diff->change)); + + if (diff->change > 1.0) + printf ("speedup\n"); + else + printf ("slowdown\n"); + + if (options->print_change_bars) + print_change_bar (fabs (diff->change), max_change, + options->use_utf); +} + +static void +test_diff_print_multi (test_diff_t *diff, + double max_change, + cairo_perf_report_options_t *options) +{ + int i; + double test_time; + double change; + + if (diff->tests[0]->size) { + printf ("%s (backend: %s-%s, size: %d)\n", + diff->tests[0]->name, + diff->tests[0]->backend, + diff->tests[0]->content, + diff->tests[0]->size); + } else { + printf ("%s (backend: %s)\n", + diff->tests[0]->name, + diff->tests[0]->backend); + } + + for (i = 0; i < diff->num_tests; i++) { + test_time = diff->tests[i]->stats.min_ticks; + if (! options->use_ticks) + test_time /= diff->tests[i]->stats.ticks_per_ms; + change = diff->max / test_time; + printf ("[%d] %6.2f: %5.2fx ", + diff->tests[i]->fileno, + diff->tests[i]->stats.min_ticks / diff->tests[i]->stats.ticks_per_ms, + change); + + if (options->print_change_bars) + print_change_bar (change, max_change, options->use_utf); + else + printf("\n"); + } + + printf("\n"); +} + +static void +cairo_perf_reports_compare (cairo_perf_report_t *reports, + int num_reports, + cairo_perf_report_options_t *options) +{ + int i; + test_report_t **tests, *min_test; + test_diff_t *diff, *diffs; + int num_diffs, max_diffs; + double max_change; + double test_time; + int seen_non_null; + cairo_bool_t printed_speedup = FALSE; + cairo_bool_t printed_slowdown = FALSE; + + assert (num_reports >= 2); + + tests = xmalloc (num_reports * sizeof (test_report_t *)); + + max_diffs = reports[0].tests_count; + for (i = 0; i < num_reports; i++) { + tests[i] = reports[i].tests; + if (reports[i].tests_count > max_diffs) + max_diffs = reports[i].tests_count; + } + + diff = diffs = xmalloc (max_diffs * sizeof (test_diff_t)); + + num_diffs = 0; + while (1) { + /* We expect iterations values of 0 when multiple raw reports + * for the same test have been condensed into the stats of the + * first. So we just skip these later reports that have no + * stats. */ + seen_non_null = 0; + for (i = 0; i < num_reports; i++) { + while (tests[i]->name && tests[i]->stats.iterations == 0) + tests[i]++; + if (tests[i]->name) + seen_non_null++; + } + + if (seen_non_null < 2) + break; + + /* Find the minimum of all current tests, (we have to do this + * in case some reports don't have a particular test). */ + for (i = 0; i < num_reports; i++) { + if (tests[i]->name) { + min_test = tests[i]; + break; + } + } + for (++i; i < num_reports; i++) { + if (tests[i]->name && + test_report_cmp_backend_then_name (tests[i], min_test) < 0) + { + min_test = tests[i]; + } + } + + /* For each report that has the current test, record it into + * the diff structure. */ + diff->num_tests = 0; + diff->tests = xmalloc (num_reports * sizeof (test_diff_t)); + for (i = 0; i < num_reports; i++) { + if (tests[i]->name && + test_report_cmp_backend_then_name (tests[i], min_test) == 0) + { + test_time = tests[i]->stats.min_ticks; + if (! options->use_ticks) + test_time /= tests[i]->stats.ticks_per_ms; + if (diff->num_tests == 0) { + diff->min = test_time; + diff->max = test_time; + } else { + if (test_time < diff->min) + diff->min = test_time; + if (test_time > diff->max) + diff->max = test_time; + } + diff->tests[diff->num_tests++] = tests[i]; + tests[i]++; + } + } + diff->change = diff->max / diff->min; + + if (num_reports == 2) { + double old_time, new_time; + if (diff->num_tests == 1) { + printf ("Only in %s: %s %s\n", + diff->tests[0]->configuration, + diff->tests[0]->backend, + diff->tests[0]->name); + continue; + } + old_time = diff->tests[0]->stats.min_ticks; + new_time = diff->tests[1]->stats.min_ticks; + if (! options->use_ticks) { + old_time /= diff->tests[0]->stats.ticks_per_ms; + new_time /= diff->tests[1]->stats.ticks_per_ms; + } + diff->change = old_time / new_time; + if (diff->change < 1.0) + diff->change = - 1.0 / diff->change; + } + + diff++; + num_diffs++; + } + if (num_diffs == 0) + goto DONE; + + if (num_reports == 2) + qsort (diffs, num_diffs, sizeof (test_diff_t), + test_diff_cmp_speedup_before_slowdown); + else + qsort (diffs, num_diffs, sizeof (test_diff_t), test_diff_cmp); + + max_change = 1.0; + for (i = 0; i < num_diffs; i++) { + if (fabs (diffs[i].change) > max_change) + max_change = fabs (diffs[i].change); + } + + if (num_reports == 2) + printf ("old: %s\n" + "new: %s\n", + diffs->tests[0]->configuration, + diffs->tests[1]->configuration); + + for (i = 0; i < num_diffs; i++) { + diff = &diffs[i]; + + /* Discard as uninteresting a change which is less than the + * minimum change required, (default may be overridden on + * command-line). */ + if (fabs (diff->change) - 1.0 < options->min_change) + continue; + + if (num_reports == 2) { + if (diff->change > 1.0 && ! printed_speedup) { + printf ("Speedups\n" + "========\n"); + printed_speedup = TRUE; + } + if (diff->change < 1.0 && ! printed_slowdown) { + printf ("Slowdowns\n" + "=========\n"); + printed_slowdown = TRUE; + } + test_diff_print_binary (diff, max_change, options); + } else { + test_diff_print_multi (diff, max_change, options); + } + } + + DONE: + for (i = 0; i < num_diffs; i++) + free (diffs[i].tests); + free (diffs); + free (tests); +} + +static void +usage (const char *argv0) +{ + char const *basename = strrchr(argv0, '/'); + basename = basename ? basename+1 : argv0; + fprintf (stderr, + "Usage: %s [options] file1 file2 [...]\n\n", + basename); + fprintf (stderr, + "Computes significant performance differences for cairo performance reports.\n" + "Each file should be the output of the cairo-perf program (or \"make perf\").\n" + "The following options are available:\n" + "\n" + "--no-utf Use ascii stars instead of utf-8 change bars.\n" + " Four stars are printed per factor of speedup.\n" + "\n" + "--no-bars Don't display change bars at all.\n\n" + "\n" + "--use-ms Use milliseconds to calculate differences.\n" + " (instead of ticks which are hardware dependent)\n" + "\n" + "--min-change threshold[%%]\n" + " Suppress all changes below the given threshold.\n" + " The default threshold of 0.05 or 5%% ignores any\n" + " speedup or slowdown of 1.05 or less. A threshold\n" + " of 0 will cause all output to be reported.\n" + ); + exit(1); +} + +static void +parse_args (int argc, + char const **argv, + cairo_perf_diff_files_args_t *args) +{ + int i; + + for (i = 1; i < argc; i++) { + if (strcmp (argv[i], "--no-utf") == 0) { + args->options.use_utf = 0; + } + else if (strcmp (argv[i], "--no-bars") == 0) { + args->options.print_change_bars = 0; + } + else if (strcmp (argv[i], "--use-ms") == 0) { + /* default */ + } + else if (strcmp (argv[i], "--use-ticks") == 0) { + args->options.use_ticks = 1; + } + else if (strcmp (argv[i], "--min-change") == 0) { + char *end = NULL; + i++; + if (i >= argc) + usage (argv[0]); + args->options.min_change = strtod (argv[i], &end); + if (*end) { + if (*end == '%') { + args->options.min_change /= 100; + } else { + usage (argv[0]); + } + } + } + else { + args->num_filenames++; + args->filenames = xrealloc (args->filenames, + args->num_filenames * sizeof (char *)); + args->filenames[args->num_filenames - 1] = argv[i]; + } + } +} + +int +main (int argc, + const char *argv[]) +{ + cairo_perf_diff_files_args_t args = { + NULL, /* filenames */ + 0, /* num_filenames */ + { + 0.05, /* min change */ + 1, /* use UTF-8? */ + 1, /* display change bars? */ + } + }; + cairo_perf_report_t *reports; + test_report_t *t; + int i; + + parse_args (argc, argv, &args); + + if (args.num_filenames < 2) + usage (argv[0]); + + reports = xmalloc (args.num_filenames * sizeof (cairo_perf_report_t)); + + for (i = 0; i < args.num_filenames; i++ ) { + cairo_perf_report_load (&reports[i], args.filenames[i], i, NULL); + printf ("[%d] %s\n", i, args.filenames[i]); + } + printf ("\n"); + + cairo_perf_reports_compare (reports, args.num_filenames, &args.options); + + /* Pointless memory cleanup, (would be a great place for talloc) */ + free (args.filenames); + for (i = 0; i < args.num_filenames; i++) { + for (t = reports[i].tests; t->name; t++) { + free (t->samples); + free (t->backend); + free (t->name); + } + free (reports[i].tests); + free (reports[i].configuration); + } + free (reports); + + return 0; +} |