diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2008-10-16 11:56:19 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2008-10-19 09:36:53 +0100 |
commit | f2ff7944264c23cbec856be3e85f240a93184f80 (patch) | |
tree | 0c14c7d36b71bcb92bf244b6eede1eec5b4aa4ea /perf/cairo-perf-diff-files.c | |
parent | 41c8eefc6d432ab213f6f405c3d6346adb7f7931 (diff) |
[perf] A crude tool to visualise performance changes across a series.
Generate a cairo-perf-diff graph for a series of commits in order to be
able to identify significant commits. Still very crude, but minimally
functional.
Diffstat (limited to 'perf/cairo-perf-diff-files.c')
-rw-r--r-- | perf/cairo-perf-diff-files.c | 458 |
1 files changed, 0 insertions, 458 deletions
diff --git a/perf/cairo-perf-diff-files.c b/perf/cairo-perf-diff-files.c index 9098c91c..9b9f5f97 100644 --- a/perf/cairo-perf-diff-files.c +++ b/perf/cairo-perf-diff-files.c @@ -27,10 +27,6 @@ #include "cairo-perf.h" -/* We use _GNU_SOURCE for getline and strndup if available. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -38,50 +34,6 @@ #include <ctype.h> #include <math.h> #include <assert.h> -#ifdef HAVE_LIBGEN_H -#include <libgen.h> -#endif - -typedef struct _test_report { - int id; - const char *configuration; - char *backend; - char *content; - char *name; - int size; - - /* 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 { - test_report_t **tests; - int num_tests; - double min; - double max; - double change; -} test_diff_t; - -typedef struct _cairo_perf_report { - char *configuration; - const char *name; - test_report_t *tests; - int tests_size; - int tests_count; -} cairo_perf_report_t; - -typedef enum { - TEST_REPORT_STATUS_SUCCESS, - TEST_REPORT_STATUS_COMMENT, - TEST_REPORT_STATUS_ERROR -} test_report_status_t; typedef struct _cairo_perf_report_options { double min_change; @@ -96,416 +48,6 @@ typedef struct _cairo_perf_diff_files_args { cairo_perf_report_options_t options; } cairo_perf_diff_files_args_t; -/* 'ssize_t' does not exist in the C standard on win32. - * We use 'ptrdiff_t', which is nearly equivalent. */ -#ifdef _MSC_VER -typedef ptrdiff_t ssize_t; -#endif - -#ifndef __USE_GNU -static ssize_t -getline (char **lineptr, size_t *n, FILE *stream); - -static char * -strndup (const char *s, size_t n); -#endif - -#ifdef _MSC_VER -static long long -strtoll(const char *nptr, char **endptr, int base); - -static char * -basename(char *path); -#endif - -/* Ad-hoc parsing, macros with a strong dependence on the calling - * context, and plenty of other ugliness is here. But at least it's - * not perl... */ -#define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR; -#define skip_char(c) \ -do { \ - if (*s && *s == (c)) { \ - s++; \ - } else { \ - parse_error ("expected '%c' but found '%c'", c, *s); \ - } \ -} while (0) -#define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++; -#define parse_int(result) \ -do { \ - (result) = strtol (s, &end, 10); \ - if (*s && end != s) { \ - s = end; \ - } else { \ - 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); \ - if (*s && end != s) { \ - s = end; \ - } else { \ - parse_error("expected floating-point value but found %s", s); \ - } \ -} while (0) -/* Here a string is simply a sequence of non-whitespace */ -#define parse_string(result) \ -do { \ - for (end = s; *end; end++) \ - if (isspace (*end)) \ - break; \ - (result) = strndup (s, end - s); \ - if ((result) == NULL) { \ - fprintf (stderr, "Out of memory.\n"); \ - exit (1); \ - } \ - s = end; \ -} while (0) - -static test_report_status_t -test_report_parse (test_report_t *report, char *line, char *configuration) -{ - 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). */ - if (*s == '\n') - return TEST_REPORT_STATUS_COMMENT; - - skip_char ('['); - skip_space (); - if (*s == '#') - return TEST_REPORT_STATUS_COMMENT; - if (*s == '*') { - s++; - is_raw = TRUE; - } else { - parse_int (report->id); - } - skip_char (']'); - - skip_space (); - - report->configuration = configuration; - parse_string (report->backend); - end = strrchr (report->backend, '-'); - if (*end) - *end++ = '\0'; - report->content = end; - - skip_space (); - - parse_string (report->name); - end = strrchr (report->name, '-'); - if (*end) - *end++ = '\0'; - report->size = atoi (end); - - skip_space (); - - report->samples = NULL; - report->samples_size = 0; - report->samples_count = 0; - - if (is_raw) { - parse_double (report->stats.ticks_per_ms); - skip_space (); - - 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 (); - - parse_double (min_time); - report->stats.ticks_per_ms = report->stats.min_ticks / min_time; - - skip_space (); - - parse_double (median_time); - report->stats.median_ticks = median_time * report->stats.ticks_per_ms; - - skip_space (); - - parse_double (report->stats.std_dev); - report->stats.std_dev /= 100.0; - skip_char ('%'); - - skip_space (); - - parse_int (report->stats.iterations); - - skip_space (); - skip_char ('\n'); - } - - return TEST_REPORT_STATUS_SUCCESS; -} - -/* We conditionally provide a custom implementation of getline and strndup - * as needed. These aren't necessary full-fledged general purpose - * implementations. They just get the job done for our purposes. - */ -#ifndef __USE_GNU -#define POORMANS_GETLINE_BUFFER_SIZE (65536) -static ssize_t -getline (char **lineptr, size_t *n, FILE *stream) -{ - if (!*lineptr) - { - *n = POORMANS_GETLINE_BUFFER_SIZE; - *lineptr = (char *) malloc (*n); - } - - if (!fgets (*lineptr, *n, stream)) - return -1; - - if (!feof (stream) && !strchr (*lineptr, '\n')) - { - fprintf (stderr, "The poor man's implementation of getline in " - __FILE__ " needs a bigger buffer. Perhaps it's " - "time for a complete implementation of getline.\n"); - exit (0); - } - - return strlen (*lineptr); -} -#undef POORMANS_GETLINE_BUFFER_SIZE - -static char * -strndup (const char *s, size_t n) -{ - size_t len; - char *sdup; - - if (!s) - return NULL; - - len = strlen (s); - len = (n < len ? n : len); - sdup = (char *) malloc (len + 1); - if (sdup) - { - memcpy (sdup, s, len); - sdup[len] = '\0'; - } - - return sdup; -} -#endif /* ifndef __USE_GNU */ - -/* We provide hereafter a win32 implementation of the basename - * and strtoll functions which are not available otherwise. - * The basename function is fully compliant to its GNU specs. - */ -#ifdef _MSC_VER -long long -strtoll(const char *nptr, char **endptr, int base) -{ - return _atoi64(nptr); -} - -static char * -basename(char *path) -{ - char *end, *s; - - end = (path + strlen(path) - 1); - while (end && (end >= path + 1) && (*end == '/')) { - *end = '\0'; - end--; - } - - s = strrchr(path, '/'); - if (s) { - if (s == end) { - return s; - } else { - return s+1; - } - } else { - return path; - } -} -#endif /* ifndef _MSC_VER */ - -static int -test_report_cmp_backend_then_name (const void *a, const void *b) -{ - const test_report_t *a_test = a; - const test_report_t *b_test = b; - - int cmp; - - cmp = strcmp (a_test->backend, b_test->backend); - if (cmp) - return cmp; - - cmp = strcmp (a_test->content, b_test->content); - if (cmp) - return cmp; - - /* A NULL name is a list-termination marker, so force it last. */ - if (a_test->name == NULL) - if (b_test->name == NULL) - return 0; - else - return 1; - else if (b_test->name == NULL) - return -1; - - cmp = strcmp (a_test->name, b_test->name); - if (cmp) - return cmp; - - if (a_test->size < b_test->size) - return -1; - if (a_test->size > b_test->size) - return 1; - - return 0; -} - -static void -cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report) -{ - 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 * sizeof (cairo_perf_ticks_t)); - } - 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; - } - } - } - if (base->samples) - _cairo_stats_compute (&base->stats, base->samples, base->samples_count); - base = next; - } -} - -static void -cairo_perf_report_load (cairo_perf_report_t *report, const char *filename) -{ - FILE *file; - test_report_status_t status; - int line_number = 0; - char *line = NULL; - size_t line_size = 0; - char *configuration; - char *dot; - char *baseName; - - configuration = xmalloc (strlen (filename) * sizeof (char) + 1); - strcpy (configuration, filename); - baseName = strdup (basename (configuration)); - report->configuration = xmalloc (strlen (filename) * sizeof (char) + 1); - strcpy(report->configuration, baseName); - free (configuration); - dot = strrchr (report->configuration, '.'); - if (dot) - *dot = '\0'; - - report->name = filename; - report->tests_size = 16; - report->tests = xmalloc (report->tests_size * sizeof (test_report_t)); - report->tests_count = 0; - - file = fopen (filename, "r"); - if (file == NULL) { - fprintf (stderr, "Failed to open %s: %s\n", - filename, strerror (errno)); - exit (1); - } - - while (1) { - if (report->tests_count == report->tests_size) { - report->tests_size *= 2; - report->tests = xrealloc (report->tests, - report->tests_size * sizeof (test_report_t)); - } - - line_number++; - if (getline (&line, &line_size, file) == -1) - break; - - status = test_report_parse (&report->tests[report->tests_count], - line, report->configuration); - if (status == TEST_REPORT_STATUS_ERROR) - fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s", - line_number, filename, line); - if (status == TEST_REPORT_STATUS_SUCCESS) - report->tests_count++; - /* Do nothing on TEST_REPORT_STATUS_COMMENT */ - } - - if (line) - free (line); - - fclose (file); - - cairo_perf_report_sort_and_compute_stats (report); - - /* Add one final report with a NULL name to terminate the list. */ - if (report->tests_count == report->tests_size) { - report->tests_size *= 2; - report->tests = xrealloc (report->tests, - report->tests_size * sizeof (test_report_t)); - } - report->tests[report->tests_count].name = NULL; -} - static int test_diff_cmp_speedup_before_slowdown (const void *a, const void *b) { |