|
1
|
|
|
from collections import Iterable |
|
2
|
|
|
|
|
3
|
|
|
import py |
|
4
|
|
|
|
|
5
|
|
|
from .utils import TIME_UNITS |
|
6
|
|
|
from .utils import slugify |
|
7
|
|
|
|
|
8
|
|
|
try: |
|
9
|
|
|
from pygal.graph.box import Box |
|
10
|
|
|
from pygal.style import DefaultStyle |
|
11
|
|
|
except ImportError as exc: |
|
12
|
|
|
raise ImportError(exc.args, "Please install pygal and pygaljs or pytest-benchmark[histogram]") |
|
13
|
|
|
|
|
14
|
|
|
|
|
15
|
|
|
class CustomBox(Box): |
|
16
|
|
|
def _box_points(self, serie, _): |
|
17
|
|
|
return serie, [serie[0], serie[6]] |
|
18
|
|
|
|
|
19
|
|
|
def _value_format(self, x): |
|
20
|
|
|
return "Min: {0[0]:.4f}\n" \ |
|
21
|
|
|
"Q1-1.5IQR: {0[1]:.4f}\n" \ |
|
22
|
|
|
"Q1: {0[2]:.4f}\nMedian: {0[3]:.4f}\nQ3: {0[4]:.4f}\n" \ |
|
23
|
|
|
"Q3+1.5IQR: {0[5]:.4f}\n" \ |
|
24
|
|
|
"Max: {0[6]:.4f}".format(x[:7]) |
|
25
|
|
|
|
|
26
|
|
|
def _format(self, x, *args): |
|
27
|
|
|
sup = super(CustomBox, self)._format |
|
28
|
|
|
if args: |
|
29
|
|
|
val = x.values |
|
30
|
|
|
else: |
|
31
|
|
|
val = x |
|
32
|
|
|
if isinstance(val, Iterable): |
|
33
|
|
|
return self._value_format(val), val[7] |
|
34
|
|
|
else: |
|
35
|
|
|
return sup(x, *args) |
|
36
|
|
|
|
|
37
|
|
|
def _tooltip_data(self, node, value, x, y, classes=None, xlabel=None): |
|
38
|
|
|
super(CustomBox, self)._tooltip_data(node, value[0], x, y, classes=classes, xlabel=None) |
|
39
|
|
|
self.svg.node(node, 'desc', class_="x_label").text = value[1] |
|
40
|
|
|
|
|
41
|
|
|
|
|
42
|
|
|
def make_plot(benchmarks, title, adjustment): |
|
43
|
|
|
class Style(DefaultStyle): |
|
44
|
|
|
colors = ["#000000" if row["path"] else DefaultStyle.colors[1] |
|
45
|
|
|
for row in benchmarks] |
|
46
|
|
|
font_family = 'Consolas, "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace' |
|
47
|
|
|
|
|
48
|
|
|
minimum = int(min(row["min"] * adjustment for row in benchmarks)) |
|
49
|
|
|
maximum = int(max( |
|
50
|
|
|
min(row["max"], row["hd15iqr"]) * adjustment |
|
51
|
|
|
for row in benchmarks |
|
52
|
|
|
) + 1) |
|
53
|
|
|
|
|
54
|
|
|
try: |
|
55
|
|
|
import pygaljs |
|
56
|
|
|
except ImportError: |
|
57
|
|
|
opts = {} |
|
58
|
|
|
else: |
|
59
|
|
|
opts = { |
|
60
|
|
|
"js": [ |
|
61
|
|
|
pygaljs.uri("2.0.x", "pygal-tooltips.js") |
|
62
|
|
|
] |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
plot = CustomBox( |
|
66
|
|
|
box_mode='tukey', |
|
67
|
|
|
x_label_rotation=-90, |
|
68
|
|
|
x_labels=["{0[name]}".format(row) for row in benchmarks], |
|
69
|
|
|
show_legend=False, |
|
70
|
|
|
title=title, |
|
71
|
|
|
x_title="Trial", |
|
72
|
|
|
y_title="Duration", |
|
73
|
|
|
style=Style, |
|
74
|
|
|
min_scale=20, |
|
75
|
|
|
max_scale=20, |
|
76
|
|
|
truncate_label=50, |
|
77
|
|
|
range=(minimum, maximum), |
|
78
|
|
|
zero=minimum, |
|
79
|
|
|
css=[ |
|
80
|
|
|
"file://style.css", |
|
81
|
|
|
"file://graph.css", |
|
82
|
|
|
"""inline: |
|
83
|
|
|
.tooltip .value { |
|
84
|
|
|
font-size: 1em !important; |
|
85
|
|
|
} |
|
86
|
|
|
.axis text { |
|
87
|
|
|
font-size: 9px !important; |
|
88
|
|
|
} |
|
89
|
|
|
""" |
|
90
|
|
|
], |
|
91
|
|
|
**opts |
|
92
|
|
|
) |
|
93
|
|
|
|
|
94
|
|
|
for row in benchmarks: |
|
95
|
|
|
serie = [row[field] * adjustment for field in ["min", "ld15iqr", "q1", "median", "q3", "hd15iqr", "max"]] |
|
96
|
|
|
serie.append(row["path"]) |
|
97
|
|
|
plot.add("{0[fullname]} - {0[rounds]} rounds".format(row), serie) |
|
98
|
|
|
return plot |
|
99
|
|
|
|
|
100
|
|
|
|
|
101
|
|
|
def make_histogram(output_prefix, name, benchmarks, unit, adjustment): |
|
102
|
|
|
if name: |
|
103
|
|
|
path = "{0}-{1}.svg".format(output_prefix, slugify(name)) |
|
104
|
|
|
title = "Speed in {0} of {1}".format(TIME_UNITS[unit], name) |
|
105
|
|
|
else: |
|
106
|
|
|
path = "{0}.svg".format(output_prefix) |
|
107
|
|
|
title = "Speed in {0}".format(TIME_UNITS[unit]) |
|
108
|
|
|
|
|
109
|
|
|
output_file = py.path.local(path).ensure() |
|
110
|
|
|
|
|
111
|
|
|
plot = make_plot( |
|
112
|
|
|
benchmarks=benchmarks, |
|
113
|
|
|
title=title, |
|
114
|
|
|
adjustment=adjustment, |
|
115
|
|
|
) |
|
116
|
|
|
plot.render_to_file(str(output_file)) |
|
117
|
|
|
return output_file |
|
118
|
|
|
|