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 < 2) 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 overriden 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 test_case_t * test_cases_from_reports (cairo_perf_report_t *reports, int num_reports) { test_case_t *cases, *c; test_report_t **tests; int i, j; int num_tests; num_tests = 0; for (i = 0; i < num_reports; i++) { for (j = 0; reports[i].tests[j].name != NULL; j++) ; if (j > num_tests) num_tests = j; } cases = xcalloc (num_tests+1, sizeof (test_case_t)); tests = xmalloc (num_reports * sizeof (test_report_t *)); for (i = 0; i < num_reports; i++) tests[i] = reports[i].tests; c = cases; while (1) { int seen_non_null; test_report_t *min_test; /* 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]; } } c->min_test = min_test; c->backend = min_test->backend; c->content = min_test->content; c->name = min_test->name; c->size = min_test->size; c->baseline = min_test->stats.min_ticks; c->min = c->max = 1.; c->shown = TRUE; name_to_color (c->name, &c->color); for (i = 0; i < num_reports; i++) { if (tests[i]->name && test_report_cmp_backend_then_name (tests[i], min_test) == 0) { tests[i]++; break; } } for (++i; i < num_reports; i++) { if (tests[i]->name && test_report_cmp_backend_then_name (tests[i], min_test) == 0) { double v = tests[i]->stats.min_ticks / c->baseline; if (v < c->min) c->min = v; if (v > c->max) c->max = v; tests[i]++; } } c++; } free (tests); return cases; }