mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-23 15:30:40 +01:00
Use "mild outliers" method to remove exceptional speed-ups and slow-downs from the graph, so that the majority of information is not lost by the scaling. Add the timing labels to the bars so that the true factor is always presented.
884 lines
22 KiB
C
884 lines
22 KiB
C
/*
|
|
* 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>
|
|
|
|
struct chart {
|
|
cairo_perf_report_t *reports;
|
|
const char **names;
|
|
|
|
cairo_t *cr;
|
|
int width, height;
|
|
int num_tests, num_reports;
|
|
double min_value, max_value;
|
|
|
|
cairo_bool_t use_html;
|
|
cairo_bool_t relative;
|
|
};
|
|
struct color {
|
|
double red, green, blue;
|
|
};
|
|
|
|
#define FONT_SIZE 12
|
|
#define PAD (FONT_SIZE/2+1)
|
|
|
|
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
static double
|
|
to_factor (double x)
|
|
{
|
|
#if 1
|
|
if (x > 1.)
|
|
return (x-1) * 100.;
|
|
else
|
|
return (1. - 1./x) * 100.;
|
|
#else
|
|
return log (x);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
_double_cmp (const void *_a, const void *_b)
|
|
{
|
|
const double *a = _a;
|
|
const double *b = _b;
|
|
|
|
if (*a > *b)
|
|
return 1;
|
|
if (*a < *b)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
trim_outliers (double *values, int num_values,
|
|
double *min, double *max)
|
|
{
|
|
double q1, q3, iqr;
|
|
double outlier_min, outlier_max;
|
|
int i;
|
|
|
|
/* First, identify any outliers, using the definition of "mild
|
|
* outliers" from:
|
|
*
|
|
* http://en.wikipedia.org/wiki/Outliers
|
|
*
|
|
* Which is that outliers are any values less than Q1 - 1.5 * IQR
|
|
* or greater than Q3 + 1.5 * IQR where Q1 and Q3 are the first
|
|
* and third quartiles and IQR is the inter-quartile range (Q3 -
|
|
* Q1).
|
|
*/
|
|
qsort (values, num_values,
|
|
sizeof (double), _double_cmp);
|
|
|
|
q1 = values[1*num_values / 4];
|
|
q3 = values[3*num_values / 4];
|
|
|
|
iqr = q3 - q1;
|
|
|
|
outlier_min = q1 - 1.5 * iqr;
|
|
outlier_max = q3 + 1.5 * iqr;
|
|
|
|
i = 0;
|
|
while (i < num_values && values[i] < outlier_min)
|
|
i++;
|
|
if (i == num_values)
|
|
return;
|
|
|
|
*min = values[i];
|
|
|
|
while (i < num_values && values[i] <= outlier_max)
|
|
i++;
|
|
|
|
*max = values[i-1];
|
|
}
|
|
|
|
static void
|
|
find_ranges (struct chart *chart)
|
|
{
|
|
test_report_t **tests, *min_test;
|
|
double *values;
|
|
int num_values, size_values;
|
|
double min = 0, max = 0;
|
|
double test_time;
|
|
int seen_non_null;
|
|
int num_tests = 0;
|
|
int i;
|
|
|
|
num_values = 0;
|
|
size_values = 64;
|
|
values = xmalloc (size_values * sizeof (double));
|
|
|
|
tests = xmalloc (chart->num_reports * sizeof (test_report_t *));
|
|
for (i = 0; i < chart->num_reports; i++)
|
|
tests[i] = chart->reports[i].tests;
|
|
|
|
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 < chart->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;
|
|
|
|
num_tests++;
|
|
|
|
/* 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 < chart->num_reports; i++) {
|
|
if (tests[i]->name) {
|
|
min_test = tests[i];
|
|
break;
|
|
}
|
|
}
|
|
for (++i; i < chart->num_reports; i++) {
|
|
if (tests[i]->name && test_report_cmp_name (tests[i], min_test) < 0)
|
|
min_test = tests[i];
|
|
}
|
|
|
|
test_time = 0;
|
|
for (i = 0; i < chart->num_reports; i++) {
|
|
double report_time = HUGE_VAL;
|
|
|
|
while (tests[i]->name &&
|
|
test_report_cmp_name (tests[i], min_test) == 0)
|
|
{
|
|
double time = tests[i]->stats.min_ticks;
|
|
if (time < report_time) {
|
|
time /= tests[i]->stats.ticks_per_ms;
|
|
if (time < report_time)
|
|
report_time = time;
|
|
}
|
|
tests[i]++;
|
|
}
|
|
|
|
if (report_time != HUGE_VAL) {
|
|
if (test_time == 0)
|
|
test_time = report_time;
|
|
|
|
if (chart->relative) {
|
|
double v = to_factor (test_time / report_time);
|
|
if (num_values == size_values) {
|
|
size_values *= 2;
|
|
values = xrealloc (values,
|
|
size_values * sizeof (double));
|
|
}
|
|
values[num_values++] = v;
|
|
if (v < min)
|
|
min = v;
|
|
if (v > max)
|
|
max = v;
|
|
} else {
|
|
if (report_time < min)
|
|
min = report_time;
|
|
if (report_time > max)
|
|
max = report_time;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (chart->relative)
|
|
trim_outliers (values, num_values, &min, &max);
|
|
chart->min_value = min;
|
|
chart->max_value = max;
|
|
chart->num_tests = num_tests;
|
|
|
|
free (values);
|
|
free (tests);
|
|
}
|
|
|
|
#define SET_COLOR(C, R, G, B) (C)->red = (R), (C)->green = (G), (C)->blue = (B)
|
|
static void
|
|
hsv_to_rgb (double h, double s, double v, struct color *color)
|
|
{
|
|
double m, n, f;
|
|
int i;
|
|
|
|
while (h < 0)
|
|
h += 6.;
|
|
while (h > 6.)
|
|
h -= 6.;
|
|
|
|
if (s < 0.)
|
|
s = 0.;
|
|
if (s > 1.)
|
|
s = 1.;
|
|
|
|
if (v < 0.)
|
|
v = 0.;
|
|
if (v > 1.)
|
|
v = 1.;
|
|
|
|
i = floor (h);
|
|
f = h - i;
|
|
if ((i & 1) == 0)
|
|
f = 1 - f;
|
|
|
|
m = v * (1 - s);
|
|
n = v * (1 - s * f);
|
|
switch(i){
|
|
default:
|
|
case 6:
|
|
case 0: SET_COLOR (color, v, n, m); break;
|
|
case 1: SET_COLOR (color, n, v, m); break;
|
|
case 2: SET_COLOR (color, m, v, n); break;
|
|
case 3: SET_COLOR (color, m, n, v); break;
|
|
case 4: SET_COLOR (color, n, m, v); break;
|
|
case 5: SET_COLOR (color, v, m, n); break;
|
|
}
|
|
}
|
|
|
|
static void set_report_color (struct chart *chart, int report)
|
|
{
|
|
struct color color;
|
|
|
|
hsv_to_rgb (6. / chart->num_reports * report, .7, .7, &color);
|
|
cairo_set_source_rgb (chart->cr, color.red, color.green, color.blue);
|
|
}
|
|
|
|
static void
|
|
test_background (struct chart *c, int test)
|
|
{
|
|
double dx, x;
|
|
|
|
dx = c->width / (double) c->num_tests;
|
|
x = dx * test;
|
|
|
|
if (test & 1)
|
|
cairo_set_source_rgba (c->cr, .2, .2, .2, .2);
|
|
else
|
|
cairo_set_source_rgba (c->cr, .8, .8, .8, .2);
|
|
|
|
cairo_rectangle (c->cr, floor (x), 0,
|
|
floor (dx + x) - floor (x), c->height);
|
|
cairo_fill (c->cr);
|
|
}
|
|
|
|
static void
|
|
add_chart (struct chart *c, int test, int report, double value)
|
|
{
|
|
double dx, dy, x;
|
|
|
|
if (fabs (value) < 0.1)
|
|
return;
|
|
|
|
set_report_color (c, report);
|
|
|
|
if (c->relative) {
|
|
cairo_text_extents_t extents;
|
|
cairo_bool_t show_label;
|
|
char buf[80];
|
|
double y;
|
|
|
|
dy = (c->height/2. - PAD) / MAX (-c->min_value, c->max_value);
|
|
/* the first report is always skipped, as it is used as the baseline */
|
|
dx = c->width / (double) (c->num_tests * c->num_reports);
|
|
x = dx * (c->num_reports * test + report - .5);
|
|
|
|
cairo_rectangle (c->cr,
|
|
floor (x), c->height / 2.,
|
|
floor (x + dx) - floor (x),
|
|
ceil (-dy*value - c->height/2.) + c->height/2.);
|
|
cairo_fill (c->cr);
|
|
|
|
cairo_save (c->cr);
|
|
cairo_set_font_size (c->cr, dx - 2);
|
|
|
|
if (value < 0) {
|
|
sprintf (buf, "%.1f", value/100 - 1);
|
|
} else {
|
|
sprintf (buf, "%.1f", value/100 + 1);
|
|
}
|
|
cairo_text_extents (c->cr, buf, &extents);
|
|
|
|
/* will it be clipped? */
|
|
y = -dy * value;
|
|
if (y < -c->height/2) {
|
|
y = -c->height/2;
|
|
} else if (y > c->height/2) {
|
|
y = c->height/2;
|
|
}
|
|
|
|
cairo_translate (c->cr,
|
|
floor (x) + (floor (x + dx) - floor (x))/2,
|
|
floor (y) + c->height/2.);
|
|
cairo_rotate (c->cr, -M_PI/2);
|
|
if (y < 0) {
|
|
cairo_move_to (c->cr, -extents.x_bearing -extents.width - 4, -extents.y_bearing/2);
|
|
show_label = y < -extents.width - 6;
|
|
} else {
|
|
cairo_move_to (c->cr, 2, -extents.y_bearing/2);
|
|
show_label = y > extents.width + 6;
|
|
}
|
|
|
|
cairo_set_source_rgb (c->cr, .95, .95, .95);
|
|
if (show_label)
|
|
cairo_show_text (c->cr, buf);
|
|
cairo_restore (c->cr);
|
|
} else {
|
|
dy = (c->height - PAD) / c->max_value;
|
|
dx = c->width / (double) (c->num_tests * (c->num_reports+1));
|
|
x = dx * ((c->num_reports+1) * test + report + .5);
|
|
|
|
cairo_rectangle (c->cr,
|
|
floor (x), c->height,
|
|
floor (x + dx) - floor (x),
|
|
floor (c->height - dy*value) - c->height);
|
|
cairo_fill (c->cr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_label (struct chart *c, int test, const char *label)
|
|
{
|
|
cairo_text_extents_t extents;
|
|
double dx, x;
|
|
|
|
cairo_save (c->cr);
|
|
dx = c->width / (double) c->num_tests;
|
|
if (dx / 2 - PAD < 6)
|
|
return;
|
|
cairo_set_font_size (c->cr, dx / 2 - PAD);
|
|
cairo_text_extents (c->cr, label, &extents);
|
|
|
|
x = (test + .5) * dx;
|
|
cairo_translate (c->cr, x, PAD / 2);
|
|
cairo_rotate (c->cr, -M_PI/2);
|
|
|
|
cairo_set_source_rgb (c->cr, .5, .5, .5);
|
|
cairo_move_to (c->cr, -extents.width, -extents.y_bearing/2);
|
|
cairo_show_text (c->cr, label);
|
|
cairo_restore (c->cr);
|
|
}
|
|
|
|
static void
|
|
add_base_line (struct chart *c)
|
|
{
|
|
double y;
|
|
|
|
cairo_save (c->cr);
|
|
cairo_set_line_width (c->cr, 2.);
|
|
if (c->relative) {
|
|
y = c->height / 2.;
|
|
} else {
|
|
y = c->height;
|
|
}
|
|
cairo_move_to (c->cr, 0, y);
|
|
cairo_line_to (c->cr, c->width, y);
|
|
cairo_set_source_rgb (c->cr, 1, 1, 1);
|
|
cairo_stroke (c->cr);
|
|
cairo_restore (c->cr);
|
|
}
|
|
|
|
static void
|
|
add_absolute_lines (struct chart *c)
|
|
{
|
|
const double dashes[] = { 2, 4 };
|
|
const double vlog_steps[] = { 10, 5, 4, 3, 2, 1, .5, .4, .3, .2, .1};
|
|
double v, y, dy;
|
|
unsigned int i;
|
|
char buf[80];
|
|
cairo_text_extents_t extents;
|
|
|
|
v = c->max_value / 2.;
|
|
|
|
for (i = 0; i < sizeof (vlog_steps) / sizeof (vlog_steps[0]); i++) {
|
|
double vlog = log (v) / log (vlog_steps[i]);
|
|
if (vlog > 1) {
|
|
v = pow (vlog_steps[i], floor (vlog));
|
|
goto done;
|
|
}
|
|
}
|
|
return;
|
|
done:
|
|
|
|
dy = (c->height - PAD) / c->max_value;
|
|
|
|
cairo_save (c->cr);
|
|
cairo_set_line_width (c->cr, 1.);
|
|
cairo_set_dash (c->cr, dashes, sizeof (dashes) / sizeof (dashes[0]), 0);
|
|
|
|
i = 0;
|
|
do {
|
|
y = c->height - ++i * v * dy;
|
|
if (y < PAD)
|
|
break;
|
|
|
|
cairo_set_font_size (c->cr, 8);
|
|
|
|
sprintf (buf, "%.0fs", i*v/1000);
|
|
cairo_text_extents (c->cr, buf, &extents);
|
|
|
|
cairo_set_source_rgba (c->cr, .75, 0, 0, .95);
|
|
cairo_move_to (c->cr, -extents.x_bearing, floor (y) - (extents.height/2 + extents.y_bearing) + .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, floor (y) - (extents.height/2 + extents.y_bearing) + .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
|
|
cairo_move_to (c->cr,
|
|
ceil (extents.width + extents.x_bearing + 2),
|
|
floor (y) + .5);
|
|
cairo_line_to (c->cr,
|
|
floor (c->width - (extents.width + extents.x_bearing + 2)),
|
|
floor (y) + .5);
|
|
cairo_stroke (c->cr);
|
|
} while (1);
|
|
|
|
cairo_restore (c->cr);
|
|
}
|
|
|
|
static void
|
|
add_relative_lines (struct chart *c)
|
|
{
|
|
const double dashes[] = { 2, 4 };
|
|
const double v_steps[] = { 10, 5, 1, .5, .1, .05, .01};
|
|
const int precision_steps[] = { 0, 0, 0, 1, 1, 2, 2};
|
|
int precision;
|
|
double v, y, dy, mid;
|
|
unsigned int i;
|
|
char buf[80];
|
|
cairo_text_extents_t extents;
|
|
|
|
v = MAX (-c->min_value, c->max_value) / 200;
|
|
|
|
for (i = 0; i < sizeof (v_steps) / sizeof (v_steps[0]); i++) {
|
|
if (v > v_steps[i]) {
|
|
v = v_steps[i];
|
|
precision = precision_steps[i];
|
|
goto done;
|
|
}
|
|
}
|
|
return;
|
|
done:
|
|
|
|
mid = c->height/2.;
|
|
dy = (mid - PAD) / MAX (-c->min_value, c->max_value);
|
|
|
|
cairo_save (c->cr);
|
|
cairo_set_line_width (c->cr, 1.);
|
|
cairo_set_dash (c->cr, dashes, sizeof (dashes) / sizeof (dashes[0]), 0);
|
|
cairo_set_font_size (c->cr, 8);
|
|
|
|
i = 0;
|
|
do {
|
|
y = ++i * v * dy * 100;
|
|
if (y > mid)
|
|
break;
|
|
|
|
sprintf (buf, "%.*fx", precision, i*v + 1);
|
|
cairo_text_extents (c->cr, buf, &extents);
|
|
|
|
cairo_set_source_rgba (c->cr, .75, 0, 0, .95);
|
|
cairo_move_to (c->cr, -extents.x_bearing, floor (mid + y) - (extents.height/2 + extents.y_bearing)+ .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, floor (mid + y) - (extents.height/2 + extents.y_bearing)+ .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
cairo_set_source_rgba (c->cr, 0, .75, 0, .95);
|
|
cairo_move_to (c->cr, -extents.x_bearing, ceil (mid - y) - (extents.height/2 + extents.y_bearing)+ .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
cairo_move_to (c->cr, c->width-extents.width+extents.x_bearing, ceil (mid - y) - (extents.height/2 + extents.y_bearing)+ .5);
|
|
cairo_show_text (c->cr, buf);
|
|
|
|
/* trim the dashes to no obscure the labels */
|
|
cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
|
|
cairo_move_to (c->cr,
|
|
ceil (extents.width + extents.x_bearing + 2),
|
|
floor (mid + y) + .5);
|
|
cairo_line_to (c->cr,
|
|
floor (c->width - (extents.width + extents.x_bearing + 2)),
|
|
floor (mid + y) + .5);
|
|
cairo_stroke (c->cr);
|
|
|
|
cairo_set_source_rgba (c->cr, 0, .75, 0, .5);
|
|
cairo_move_to (c->cr,
|
|
ceil (extents.width + extents.x_bearing + 2),
|
|
ceil (mid - y) + .5);
|
|
cairo_line_to (c->cr,
|
|
floor (c->width - (extents.width + extents.x_bearing + 2)),
|
|
ceil (mid - y) + .5);
|
|
cairo_stroke (c->cr);
|
|
|
|
} while (1);
|
|
|
|
cairo_restore (c->cr);
|
|
}
|
|
|
|
static void
|
|
add_slower_faster_guide (struct chart *c)
|
|
{
|
|
cairo_text_extents_t extents;
|
|
|
|
cairo_save (c->cr);
|
|
|
|
cairo_set_font_size (c->cr, FONT_SIZE);
|
|
|
|
cairo_text_extents (c->cr, "FASTER", &extents);
|
|
cairo_set_source_rgba (c->cr, 0, .75, 0, .5);
|
|
cairo_move_to (c->cr,
|
|
c->width/4. - extents.width/2. + extents.x_bearing,
|
|
1 - extents.y_bearing);
|
|
cairo_show_text (c->cr, "FASTER");
|
|
cairo_move_to (c->cr,
|
|
3*c->width/4. - extents.width/2. + extents.x_bearing,
|
|
1 - extents.y_bearing);
|
|
cairo_show_text (c->cr, "FASTER");
|
|
|
|
cairo_text_extents (c->cr, "SLOWER", &extents);
|
|
cairo_set_source_rgba (c->cr, .75, 0, 0, .5);
|
|
cairo_move_to (c->cr,
|
|
c->width/4. - extents.width/2. + extents.x_bearing,
|
|
c->height - 1);
|
|
cairo_show_text (c->cr, "SLOWER");
|
|
cairo_move_to (c->cr,
|
|
3*c->width/4. - extents.width/2. + extents.x_bearing,
|
|
c->height - 1);
|
|
cairo_show_text (c->cr, "SLOWER");
|
|
|
|
cairo_restore (c->cr);
|
|
}
|
|
|
|
static void
|
|
cairo_perf_reports_compare (struct chart *chart, cairo_bool_t print)
|
|
{
|
|
test_report_t **tests, *min_test;
|
|
double test_time, best_time;
|
|
int num_test = 0;
|
|
int seen_non_null;
|
|
int i;
|
|
|
|
tests = xmalloc (chart->num_reports * sizeof (test_report_t *));
|
|
for (i = 0; i < chart->num_reports; i++)
|
|
tests[i] = chart->reports[i].tests;
|
|
|
|
if (print) {
|
|
if (chart->use_html) {
|
|
printf ("<table style=\"text-align:right\" cellspacing=\"4\">\n");
|
|
printf ("<tr><td></td>");
|
|
for (i = 0; i < chart->num_reports; i++) {
|
|
printf ("<td>%s</td>", chart->names[i] ? chart->names[i] : "");
|
|
}
|
|
printf ("</tr>\n");
|
|
}
|
|
}
|
|
|
|
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 < chart->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 < chart->num_reports; i++) {
|
|
if (tests[i]->name) {
|
|
min_test = tests[i];
|
|
break;
|
|
}
|
|
}
|
|
for (++i; i < chart->num_reports; i++) {
|
|
if (tests[i]->name && test_report_cmp_name (tests[i], min_test) < 0)
|
|
min_test = tests[i];
|
|
}
|
|
|
|
add_label (chart, num_test, min_test->name);
|
|
if (print) {
|
|
if (chart->use_html) {
|
|
printf ("<tr><td>%s</td>", min_test->name);
|
|
} else {
|
|
if (min_test->size) {
|
|
printf ("%16s, size %4d:\n",
|
|
min_test->name,
|
|
min_test->size);
|
|
} else {
|
|
printf ("%26s:",
|
|
min_test->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
test_time = 0;
|
|
best_time = HUGE_VAL;
|
|
for (i = 0; i < chart->num_reports; i++) {
|
|
test_report_t *initial = tests[i];
|
|
double report_time = HUGE_VAL;
|
|
|
|
while (tests[i]->name &&
|
|
test_report_cmp_name (tests[i], min_test) == 0)
|
|
{
|
|
double time = tests[i]->stats.min_ticks;
|
|
if (time < report_time) {
|
|
time /= tests[i]->stats.ticks_per_ms;
|
|
if (time < report_time)
|
|
report_time = time;
|
|
}
|
|
tests[i]++;
|
|
}
|
|
|
|
if (test_time == 0 && report_time != HUGE_VAL)
|
|
test_time = report_time;
|
|
if (report_time < best_time)
|
|
best_time = report_time;
|
|
|
|
tests[i] = initial;
|
|
}
|
|
|
|
for (i = 0; i < chart->num_reports; i++) {
|
|
double report_time = HUGE_VAL;
|
|
|
|
while (tests[i]->name &&
|
|
test_report_cmp_name (tests[i], min_test) == 0)
|
|
{
|
|
double time = tests[i]->stats.min_ticks;
|
|
if (time > 0) {
|
|
time /= tests[i]->stats.ticks_per_ms;
|
|
if (time < report_time)
|
|
report_time = time;
|
|
}
|
|
tests[i]++;
|
|
}
|
|
|
|
if (print) {
|
|
if (chart->use_html) {
|
|
if (report_time < HUGE_VAL) {
|
|
if (report_time / best_time < 1.01) {
|
|
printf ("<td><strong>%.1f</strong></td>", report_time/1000);
|
|
} else {
|
|
printf ("<td>%.1f</td>", report_time/1000);
|
|
}
|
|
} else {
|
|
printf ("<td></td>");
|
|
}
|
|
} else {
|
|
if (report_time < HUGE_VAL)
|
|
printf (" %6.1f", report_time/1000);
|
|
else
|
|
printf (" ---");
|
|
}
|
|
}
|
|
|
|
if (report_time < HUGE_VAL) {
|
|
if (chart->relative) {
|
|
add_chart (chart, num_test, i,
|
|
to_factor (test_time / report_time));
|
|
} else {
|
|
add_chart (chart, num_test, i, report_time);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (print) {
|
|
if (chart->use_html) {
|
|
printf ("</tr>\n");
|
|
} else {
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
num_test++;
|
|
}
|
|
free (tests);
|
|
|
|
if (print) {
|
|
if (chart->use_html)
|
|
printf ("</table>\n");
|
|
|
|
printf ("\n");
|
|
for (i = 0; i < chart->num_reports; i++) {
|
|
if (chart->names[i]) {
|
|
printf ("[%s] %s\n",
|
|
chart->names[i], chart->reports[i].configuration);
|
|
} else {
|
|
printf ("[%d] %s\n",
|
|
i, chart->reports[i].configuration);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_legend (struct chart *chart)
|
|
{
|
|
cairo_text_extents_t extents;
|
|
const char *str;
|
|
int i, x, y;
|
|
|
|
cairo_set_font_size (chart->cr, FONT_SIZE);
|
|
|
|
x = PAD;
|
|
y = chart->height + PAD;
|
|
for (i = chart->relative; i < chart->num_reports; i++) {
|
|
str = chart->names[i] ?
|
|
chart->names[i] : chart->reports[i].configuration;
|
|
|
|
set_report_color (chart, i);
|
|
|
|
cairo_rectangle (chart->cr, x, y + 6, 8, 8);
|
|
cairo_fill (chart->cr);
|
|
|
|
cairo_set_source_rgb (chart->cr, 1, 1, 1);
|
|
cairo_move_to (chart->cr, x + 10, y + FONT_SIZE + PAD / 2.);
|
|
cairo_text_extents (chart->cr, str, &extents);
|
|
cairo_show_text (chart->cr, str);
|
|
|
|
x += 10 + 2 * PAD + ceil (extents.width);
|
|
}
|
|
|
|
if (chart->relative) {
|
|
char buf[80];
|
|
|
|
str = chart->names[0] ?
|
|
chart->names[0] : chart->reports[0].configuration;
|
|
|
|
sprintf (buf, "(relative to %s)", str);
|
|
cairo_text_extents (chart->cr, buf, &extents);
|
|
|
|
cairo_set_source_rgb (chart->cr, 1, 1, 1);
|
|
cairo_move_to (chart->cr,
|
|
chart->width - 1 - extents.width,
|
|
y + FONT_SIZE + PAD / 2.);
|
|
cairo_show_text (chart->cr, buf);
|
|
}
|
|
}
|
|
|
|
int
|
|
main (int argc, const char *argv[])
|
|
{
|
|
cairo_surface_t *surface;
|
|
struct chart chart;
|
|
test_report_t *t;
|
|
int i;
|
|
|
|
chart.use_html = 0;
|
|
chart.width = 640;
|
|
chart.height = 480;
|
|
|
|
chart.reports = xcalloc (argc-1, sizeof (cairo_perf_report_t));
|
|
chart.names = xcalloc (argc-1, sizeof (cairo_perf_report_t));
|
|
|
|
chart.num_reports = 0;
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp (argv[i], "--html") == 0) {
|
|
chart.use_html = 1;
|
|
} else if (strncmp (argv[i], "--width=", 8) == 0) {
|
|
chart.width = atoi (argv[i] + 8);
|
|
} else if (strncmp (argv[i], "--height=", 9) == 0) {
|
|
chart.height = atoi (argv[i] + 9);
|
|
} else if (strcmp (argv[i], "--name") == 0) {
|
|
if (i + 1 < argc)
|
|
chart.names[chart.num_reports] = argv[++i];
|
|
} else if (strncmp (argv[i], "--name=", 7) == 0) {
|
|
chart.names[chart.num_reports] = argv[i] + 7;
|
|
} else {
|
|
cairo_perf_report_load (&chart.reports[chart.num_reports++],
|
|
argv[i],
|
|
test_report_cmp_name);
|
|
}
|
|
}
|
|
|
|
for (chart.relative = 0; chart.relative <= 1; chart.relative++) {
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
chart.width,
|
|
chart.height + (FONT_SIZE + PAD) + 2*PAD);
|
|
chart.cr = cairo_create (surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
cairo_set_source_rgb (chart.cr, 0, 0, 0);
|
|
cairo_paint (chart.cr);
|
|
|
|
find_ranges (&chart);
|
|
|
|
for (i = 0; i < chart.num_tests; i++)
|
|
test_background (&chart, i);
|
|
if (chart.relative) {
|
|
add_relative_lines (&chart);
|
|
add_slower_faster_guide (&chart);
|
|
} else
|
|
add_absolute_lines (&chart);
|
|
|
|
cairo_save (chart.cr);
|
|
cairo_rectangle (chart.cr, 0, 0, chart.width, chart.height);
|
|
cairo_clip (chart.cr);
|
|
cairo_perf_reports_compare (&chart, !chart.relative);
|
|
cairo_restore (chart.cr);
|
|
|
|
add_base_line (&chart);
|
|
add_legend (&chart);
|
|
|
|
cairo_surface_write_to_png (cairo_get_target (chart.cr),
|
|
chart.relative ?
|
|
"cairo-perf-chart-relative.png" :
|
|
"cairo-perf-chart-absolute.png");
|
|
cairo_destroy (chart.cr);
|
|
}
|
|
|
|
/* Pointless memory cleanup, (would be a great place for talloc) */
|
|
for (i = 0; i < chart.num_reports; i++) {
|
|
for (t = chart.reports[i].tests; t->name; t++) {
|
|
free (t->samples);
|
|
free (t->backend);
|
|
free (t->name);
|
|
}
|
|
free (chart.reports[i].tests);
|
|
free (chart.reports[i].configuration);
|
|
}
|
|
free (chart.names);
|
|
free (chart.reports);
|
|
|
|
return 0;
|
|
}
|