Repository navigation
Expand file tree
/
Copy pathbenchmark-tables.py
More file actions
129 lines (99 loc) · 4.42 KB
/
Copy pathbenchmark-tables.py
File metadata and controls
129 lines (99 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Assume we did::
#
# tox -e py36 -- -m benchmark --benchmark-json=comparison.json --compare-other-engines
#
# Read back that JSON outcome and produce two reST tables summarizing the
# comparison.
#
import json
from collections import defaultdict, namedtuple
CONTENDERS = 'rapidjson,rapidjson_nativenumbers,ujson,simplejson,json,yajl'.split(',')
S_HEADERS = 'rapidjson,native [1]_,ujson [2]_,simplejson [3]_,stdlib [4]_,yajl [5]_'.split(',')
D_HEADERS = 'rapidjson,native,ujson,simplejson,stdlib,yajl'.split(',')
assert len(CONTENDERS) == len(S_HEADERS) == len(D_HEADERS)
FOOTNOTES = ['rapidjson with ``number_mode=NM_NATIVE``',
'`ujson 1.35 <https://pypi.python.org/pypi/ujson/1.35>`__',
'`simplejson 3.11.1 <https://pypi.python.org/pypi/simplejson/3.11.1>`__',
'Python 3.6 standard library',
'`yajl 0.3.5 <https://pypi.python.org/pypi/yajl/0.3.5>`__']
Timings = namedtuple('Timings', 'min, max, mean, rounds, median')
Benchmark = namedtuple('Benchmark', 'group, name, contender')
SingleValue = namedtuple('SingleValue', CONTENDERS)
class Comparison:
def __init__(self, fname):
self.timings = timings = {}
with open(fname) as f:
benchmark_data = json.load(f)
for bm in benchmark_data['benchmarks']:
group = bm['group']
desc = bm['name'].split('[', 1)[1][:-1]
contender, name = desc.split('-')
if contender == 'stdlib json':
contender = 'json'
if contender in CONTENDERS:
bmark = Benchmark(group, name, contender)
stats = bm['stats']
timing = Timings(stats['min'], stats['max'], stats['mean'],
stats['rounds'], stats['median'])
timings[bmark] = timing
def focalize(self, group, fieldname):
values = defaultdict(dict)
for bmark, timing in self.timings.items():
if bmark.group == group:
values[bmark.name][bmark.contender] = getattr(timing, fieldname)
return {n: SingleValue(**values[n]) for n in values}
def normalize_and_summarize(self, group, fieldname, by='rapidjson'):
result = {}
values = self.focalize(group, fieldname)
sums = defaultdict(float)
for name, value in values.items():
for c in CONTENDERS:
sums[c] += getattr(value, c)
result[name] = SingleValue(*[(getattr(value, c) / getattr(value, by))
for c in CONTENDERS])
sum = SingleValue(**sums)
return result, SingleValue(*[(getattr(sum, c) / getattr(sum, by))
for c in CONTENDERS])
def tabulate(self, group, fieldname, headers, normalize_by='rapidjson', omit='rapidjson'):
normalized, sum = self.normalize_and_summarize(group, fieldname, normalize_by)
nc = len(CONTENDERS) - 1
widths = [max((len(n)+4 for n in normalized))] + \
[max((len(h) for h in headers))] * nc
cells = ['-' * (width+2) for width in widths]
seprow = '+' + '+'.join(cells) + '+'
cells = ['=' * (width+2) for width in widths]
headseprow = '+' + '+'.join(cells) + '+'
cells = [' {:^%d} ' % width for width in widths]
rowfmt = '|' + '|'.join(cells) + '|'
print(seprow)
print(rowfmt.format(*([group] + [headers[i] for i, c in enumerate(CONTENDERS)
if c != omit])))
print(headseprow)
def printrow(name, row):
values = [getattr(row, c) for c in CONTENDERS if c != omit]
best = min(values)
if best > getattr(row, omit):
name = '**' + name + '**'
best = 0
fvalues = [('**%.2f**' if v==best else '%.2f') % v for v in values]
print(rowfmt.format(*([name] + fvalues)))
print(seprow)
for n in sorted(normalized):
printrow(n, normalized[n])
printrow('overall', sum)
def main():
import sys
if len(sys.argv) > 1:
fname = sys.argv[1]
else:
fname = 'comparison.json'
comparison = Comparison(fname)
comparison.tabulate('serialize', 'mean', S_HEADERS)
comparison.tabulate('deserialize', 'mean', D_HEADERS)
idx = 1
print()
for fn in FOOTNOTES:
print('.. [%d] %s' % (idx, fn))
idx += 1
if __name__ == '__main__':
main()