From 6d5df0e3e2e9804cede77cfd0d3659bd2d36a918 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 7 Nov 2006 13:06:01 -0800 Subject: [PATCH] cairo-perf-diff-files: Add support for diffing "raw" files from "cairo-perf -r" --- perf/cairo-perf-diff-files.c | 159 ++++++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 32 deletions(-) diff --git a/perf/cairo-perf-diff-files.c b/perf/cairo-perf-diff-files.c index 8dbe49b7d..b4d9dc2a5 100644 --- a/perf/cairo-perf-diff-files.c +++ b/perf/cairo-perf-diff-files.c @@ -25,6 +25,8 @@ * Authors: Carl Worth */ +#include "cairo-perf.h" + /* We use _GNU_SOURCE for getline. If someone wants to avoid that * dependence they could conditionally provide a custom implementation * of getline instead. */ @@ -44,11 +46,16 @@ typedef struct _test_report { char *content; char *name; int size; - double min_ticks; - double min_time; - double median_time; - double std_dev; - int iterations; + + /* The samples only exists for "raw" reports */ + cairo_perf_ticks_t *samples; + unsigned int samples_size; + unsigned int samples_count; + + /* The stats are either read directly or computed from samples. + * If the stats have not yet been computed from samples, then + * iterations will be 0. */ + cairo_stats_t stats; } test_report_t; typedef struct _test_diff { @@ -92,6 +99,15 @@ do { \ parse_error("expected integer but found %s", s); \ } \ } while (0) +#define parse_long_long(result) \ +do { \ + (result) = strtoll (s, &end, 10); \ + if (*s && end != s) { \ + s = end; \ + } else { \ + parse_error("expected integer but found %s", s); \ + } \ +} while (0) #define parse_double(result) \ do { \ (result) = strtod (s, &end); \ @@ -120,6 +136,8 @@ test_report_parse (test_report_t *report, char *line) { char *end; char *s = line; + cairo_bool_t is_raw = FALSE; + double min_time, median_time; /* The code here looks funny unless you understand that these are * all macro calls, (and then the code just looks sick). */ @@ -130,7 +148,12 @@ test_report_parse (test_report_t *report, char *line) skip_space (); if (*s == '#') return TEST_REPORT_STATUS_COMMENT; - parse_int (report->id); + if (*s == '*') { + s++; + is_raw = TRUE; + } else { + parse_int (report->id); + } skip_char (']'); skip_space (); @@ -151,28 +174,52 @@ test_report_parse (test_report_t *report, char *line) skip_space (); - parse_double (report->min_ticks); + report->samples = NULL; + report->samples_size = 0; + report->samples_count = 0; - skip_space (); + if (is_raw) { + parse_double (report->stats.ticks_per_ms); + skip_space (); - parse_double (report->min_time); + report->samples_size = 5; + report->samples = xmalloc (report->samples_size * sizeof (cairo_perf_ticks_t)); + do { + if (report->samples_count == report->samples_size) { + report->samples_size *= 2; + report->samples = xrealloc (report->samples, + report->samples_size * sizeof (cairo_perf_ticks_t)); + } + parse_long_long (report->samples[report->samples_count++]); + skip_space (); + } while (*s && *s != '\n'); + report->stats.iterations = 0; + skip_char ('\n'); + } else { + parse_double (report->stats.min_ticks); + skip_space (); - skip_space (); + parse_double (min_time); + report->stats.ticks_per_ms = report->stats.min_ticks / min_time; - parse_double (report->median_time); + skip_space (); - skip_space (); + parse_double (median_time); + report->stats.median_ticks = median_time * report->stats.ticks_per_ms; - parse_double (report->std_dev); - report->std_dev /= 100.0; - skip_char ('%'); + skip_space (); - skip_space (); + parse_double (report->stats.std_dev); + report->stats.std_dev /= 100.0; + skip_char ('%'); - parse_int (report->iterations); + skip_space (); - skip_space (); - skip_char ('\n'); + parse_int (report->stats.iterations); + + skip_space (); + skip_char ('\n'); + } return TEST_REPORT_STATUS_SUCCESS; } @@ -284,10 +331,46 @@ test_report_cmp_backend_then_name (const void *a, const void *b) } static void -cairo_perf_report_sort_by_backend_then_name (cairo_perf_report_t *report) +cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report) { - qsort (report->tests, report->tests_count, sizeof (test_report_t), + test_report_t *base, *next, *last, *t; + + /* First we sort, since the diff needs both lists in the same + * order */ + qsort (report->tests, report->tests_count, sizeof (test_report_t), test_report_cmp_backend_then_name); + + /* The sorting also brings all related raw reports together so we + * can condense them and compute the stats. + */ + base = &report->tests[0]; + last = &report->tests[report->tests_count - 1]; + while (base <= last) { + next = base+1; + if (next <= last) { + while (next <= last && + test_report_cmp_backend_then_name (base, next) == 0) + { + next++; + } + if (next != base) { + unsigned int new_samples_count = base->samples_count; + for (t = base + 1; t < next; t++) + new_samples_count += t->samples_count; + if (new_samples_count > base->samples_size) { + base->samples_size = new_samples_count; + base->samples = xrealloc (base->samples, base->samples_size); + } + for (t = base + 1; t < next; t++) { + memcpy (&base->samples[base->samples_count], t->samples, + t->samples_count * sizeof (cairo_perf_ticks_t)); + base->samples_count += t->samples_count; + } + } + } + _cairo_stats_compute (&base->stats, base->samples, base->samples_count); + base = next; + } } #define CHANGE_BAR_WIDTH 70 @@ -341,20 +424,30 @@ cairo_perf_report_diff (cairo_perf_report_t *old, int printed_speedup = 0, printed_slowdown = 0; double change, max_change; - diffs = malloc (MAX (old->tests_count, new->tests_count) * sizeof (test_diff_t)); - if (diffs == NULL) { - fprintf (stderr, "Out of memory.\n"); - exit (1); - } + diffs = xmalloc (MAX (old->tests_count, new->tests_count) * sizeof (test_diff_t)); - cairo_perf_report_sort_by_backend_then_name (old); - cairo_perf_report_sort_by_backend_then_name (new); + cairo_perf_report_sort_and_compute_stats (old); + cairo_perf_report_sort_and_compute_stats (new); i_old = 0; i_new = 0; while (i_old < old->tests_count && i_new < new->tests_count) { o = &old->tests[i_old]; n = &new->tests[i_new]; + + /* We expect iterations values of 0 when mutltiple 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. */ + if (o->stats.iterations == 0) { + i_old++; + continue; + } + if (n->stats.iterations == 0) { + i_new++; + continue; + } + cmp = test_report_cmp_backend_then_name (o, n); if (cmp < 0) { fprintf (stderr, "Only in old: %s %s\n", o->backend, o->name); @@ -369,7 +462,7 @@ cairo_perf_report_diff (cairo_perf_report_t *old, diffs[num_diffs].old = o; diffs[num_diffs].new = n; - diffs[num_diffs].speedup = o->min_ticks / n->min_ticks; + diffs[num_diffs].speedup = (double) o->stats.min_ticks / n->stats.min_ticks; num_diffs++; i_old++; @@ -402,7 +495,7 @@ cairo_perf_report_diff (cairo_perf_report_t *old, /* Also discard as uninteresting if the change is less than * the sum each of the standard deviations. */ - if (change - 1.0 < diff->old->std_dev + diff->new->std_dev) + if (change - 1.0 < diff->old->stats.std_dev + diff->new->stats.std_dev) continue; if (diff->speedup > 1.0 && ! printed_speedup) { @@ -419,8 +512,10 @@ cairo_perf_report_diff (cairo_perf_report_t *old, printf ("%5s-%-4s %26s-%-3d %6.2f %4.2f%% -> %6.2f %4.2f%%: %5.2fx ", diff->old->backend, diff->old->content, diff->old->name, diff->old->size, - diff->old->min_time, diff->old->std_dev * 100, - diff->new->min_time, diff->new->std_dev * 100, + diff->old->stats.min_ticks / diff->old->stats.ticks_per_ms, + diff->old->stats.std_dev * 100, + diff->new->stats.min_ticks / diff->new->stats.ticks_per_ms, + diff->new->stats.std_dev * 100, change); if (diff->speedup > 1.0)