summaryrefslogtreecommitdiff
path: root/perf/cairo-perf-compare-backends.c
diff options
context:
space:
mode:
Diffstat (limited to 'perf/cairo-perf-compare-backends.c')
-rw-r--r--perf/cairo-perf-compare-backends.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/perf/cairo-perf-compare-backends.c b/perf/cairo-perf-compare-backends.c
new file mode 100644
index 000000000..ff7359e1a
--- /dev/null
+++ b/perf/cairo-perf-compare-backends.c
@@ -0,0 +1,398 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
+ *
+ * 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>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#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;
+} 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 (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]);
+}
+
+static void
+test_diff_print (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 != 0) {
+ printf ("(%s, size: %d)\n",
+ diff->tests[0]->name,
+ diff->tests[0]->size);
+ } else {
+ printf ("(%s)\n", diff->tests[0]->name);
+ }
+
+ for (i = 0; i < diff->num_tests; i++) {
+ test_time = diff->tests[i]->stats.min_ticks;
+ test_time /= diff->tests[i]->stats.ticks_per_ms;
+ change = diff->max / test_time;
+ printf ("%8s-%s-%s\t%6.2f: %5.2fx ",
+ diff->tests[i]->backend,
+ diff->tests[i]->content,
+ diff->tests[i]->configuration,
+ 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);
+ 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;
+
+ 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) {
+ int num_tests;
+
+ /* 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)
+ 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_name (tests[i], min_test) < 0)
+ min_test = tests[i];
+ }
+
+ num_tests = 0;
+ for (i = 0; i < num_reports; i++) {
+ test_report_t *test;
+ int n = 0;
+
+ test = tests[i];
+ while (test[n].name &&
+ test_report_cmp_name (&test[n], min_test) == 0)
+ {
+ n++;
+ }
+
+ num_tests += n;
+ }
+
+ /* For each report that has the current test, record it into
+ * the diff structure. */
+ diff->num_tests = 0;
+ diff->tests = xmalloc (num_tests * sizeof (test_diff_t));
+ for (i = 0; i < num_reports; i++) {
+ while (tests[i]->name &&
+ test_report_cmp_name (tests[i], min_test) == 0)
+ {
+ test_time = tests[i]->stats.min_ticks;
+ if (test_time > 0) {
+ 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;
+
+ diff++;
+ num_diffs++;
+ }
+ if (num_diffs == 0)
+ goto DONE;
+
+ 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);
+ }
+
+ 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;
+
+ test_diff_print (diff, max_change, options);
+ }
+
+ for (i = 0; i < num_diffs; i++)
+ free (diffs[i].tests);
+ DONE:
+ 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] file [...]\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], "--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) {
+ reports = xcalloc (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,
+ test_report_cmp_name);
+ printf ("loaded: %s, %d tests\n",
+ args.filenames[i], reports[i].tests_count);
+ }
+ } else {
+ args.num_filenames = 1;
+ reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t));
+ cairo_perf_report_load (&reports[0], NULL, 0, test_report_cmp_name);
+ }
+
+ 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;
+}