]>
Commit | Line | Data |
---|---|---|
25d52472 | 1 | #!/usr/bin/python |
04277e02 | 2 | # Copyright (C) 2017-2019 Free Software Foundation, Inc. |
25d52472 SP |
3 | # This file is part of the GNU C Library. |
4 | # | |
5 | # The GNU C Library is free software; you can redistribute it and/or | |
6 | # modify it under the terms of the GNU Lesser General Public | |
7 | # License as published by the Free Software Foundation; either | |
8 | # version 2.1 of the License, or (at your option) any later version. | |
9 | # | |
10 | # The GNU C Library is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | # Lesser General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU Lesser General Public | |
16 | # License along with the GNU C Library; if not, see | |
17 | # <http://www.gnu.org/licenses/>. | |
18 | """Compare results of string functions | |
19 | ||
20 | Given a string benchmark result file, print a table with comparisons with a | |
21 | baseline. The baseline is the first function, which typically is the builtin | |
22 | function. | |
23 | """ | |
dd3e86ad SP |
24 | import matplotlib as mpl |
25 | mpl.use('Agg') | |
b115e819 | 26 | |
25d52472 SP |
27 | import sys |
28 | import os | |
29 | import json | |
30 | import pylab | |
06b1de23 | 31 | import argparse |
d67d634b | 32 | import traceback |
25d52472 SP |
33 | |
34 | try: | |
35 | import jsonschema as validator | |
36 | except ImportError: | |
37 | print('Could not find jsonschema module.') | |
38 | raise | |
39 | ||
40 | ||
41 | def parse_file(filename, schema_filename): | |
a650b05e LS |
42 | try: |
43 | with open(schema_filename, 'r') as schemafile: | |
44 | schema = json.load(schemafile) | |
45 | with open(filename, 'r') as benchfile: | |
46 | bench = json.load(benchfile) | |
47 | validator.validate(bench, schema) | |
48 | return bench | |
d67d634b SP |
49 | except: |
50 | print(traceback.format_exc(limit=1)) | |
a650b05e | 51 | sys.exit(os.EX_NOINPUT) |
25d52472 SP |
52 | |
53 | def draw_graph(f, v, ifuncs, results): | |
54 | """Plot graphs for functions | |
55 | ||
56 | Plot line graphs for each of the ifuncs | |
57 | ||
58 | Args: | |
59 | f: Function name | |
60 | v: Benchmark variant for the function. | |
61 | ifuncs: List of ifunc names | |
62 | results: Dictionary of results for each test criterion | |
63 | """ | |
140647ea | 64 | print('Generating graph for %s, variant \'%s\'' % (f, v)) |
25d52472 SP |
65 | xkeys = results.keys() |
66 | ||
67 | pylab.clf() | |
68 | fig = pylab.figure(frameon=False) | |
69 | fig.set_size_inches(32, 18) | |
70 | pylab.ylabel('Performance improvement from base') | |
71 | X = range(len(xkeys)) | |
72 | pylab.xticks(X, xkeys) | |
73 | ||
74 | i = 0 | |
75 | ||
76 | while i < len(ifuncs): | |
77 | Y = [results[k][i] for k in xkeys] | |
78 | lines = pylab.plot(X, Y, label=':'+ifuncs[i]) | |
79 | i = i + 1 | |
80 | ||
81 | pylab.legend() | |
82 | pylab.grid() | |
83 | pylab.savefig('%s-%s.png' % (f, v), bbox_inches='tight') | |
84 | ||
85 | ||
cb8f6aff | 86 | def process_results(results, attrs, funcs, base_func, graph, no_diff, no_header): |
25d52472 SP |
87 | """ Process results and print them |
88 | ||
89 | Args: | |
90 | results: JSON dictionary of results | |
91 | attrs: Attributes that form the test criteria | |
cb8f6aff | 92 | funcs: Functions that are selected |
25d52472 SP |
93 | """ |
94 | ||
95 | for f in results['functions'].keys(): | |
195abbf4 | 96 | |
5a6547b7 | 97 | v = results['functions'][f]['bench-variant'] |
5a6547b7 | 98 | |
cb8f6aff L |
99 | selected = {} |
100 | index = 0 | |
b115e819 | 101 | base_index = 0 |
cb8f6aff L |
102 | if funcs: |
103 | ifuncs = [] | |
104 | first_func = True | |
105 | for i in results['functions'][f]['ifuncs']: | |
106 | if i in funcs: | |
107 | if first_func: | |
108 | base_index = index | |
109 | first_func = False | |
110 | selected[index] = 1 | |
111 | ifuncs.append(i) | |
112 | else: | |
113 | selected[index] = 0 | |
114 | index += 1 | |
115 | else: | |
116 | ifuncs = results['functions'][f]['ifuncs'] | |
117 | for i in ifuncs: | |
118 | selected[index] = 1 | |
119 | index += 1 | |
120 | ||
b115e819 | 121 | if base_func: |
a650b05e LS |
122 | try: |
123 | base_index = results['functions'][f]['ifuncs'].index(base_func) | |
124 | except ValueError: | |
125 | sys.stderr.write('Invalid -b "%s" parameter. Options: %s.\n' % | |
126 | (base_func, ', '.join(results['functions'][f]['ifuncs']))) | |
127 | sys.exit(os.EX_DATAERR) | |
b115e819 | 128 | |
195abbf4 LS |
129 | if not no_header: |
130 | print('Function: %s' % f) | |
131 | print('Variant: %s' % v) | |
cb8f6aff | 132 | print("%36s%s" % (' ', '\t'.join(ifuncs))) |
195abbf4 LS |
133 | print("=" * 120) |
134 | ||
25d52472 SP |
135 | graph_res = {} |
136 | for res in results['functions'][f]['results']: | |
a650b05e LS |
137 | try: |
138 | attr_list = ['%s=%s' % (a, res[a]) for a in attrs] | |
139 | except KeyError as ke: | |
140 | sys.stderr.write('Invalid -a %s parameter. Options: %s.\n' | |
141 | % (ke, ', '.join([a for a in res.keys() if a != 'timings']))) | |
142 | sys.exit(os.EX_DATAERR) | |
b115e819 | 143 | i = 0 |
5a6547b7 SP |
144 | key = ', '.join(attr_list) |
145 | sys.stdout.write('%36s: ' % key) | |
25d52472 SP |
146 | graph_res[key] = res['timings'] |
147 | for t in res['timings']: | |
cb8f6aff L |
148 | if selected[i]: |
149 | sys.stdout.write ('%12.2f' % t) | |
150 | if not no_diff: | |
151 | if i != base_index: | |
152 | base = res['timings'][base_index] | |
153 | diff = (base - t) * 100 / base | |
154 | sys.stdout.write (' (%6.2f%%)' % diff) | |
155 | sys.stdout.write('\t') | |
b115e819 | 156 | i = i + 1 |
25d52472 | 157 | print('') |
140647ea SP |
158 | |
159 | if graph: | |
160 | draw_graph(f, v, results['functions'][f]['ifuncs'], graph_res) | |
25d52472 SP |
161 | |
162 | ||
163 | def main(args): | |
164 | """Program Entry Point | |
165 | ||
166 | Take a string benchmark output file and compare timings. | |
167 | """ | |
25d52472 | 168 | |
b115e819 | 169 | base_func = None |
06b1de23 SP |
170 | filename = args.input |
171 | schema_filename = args.schema | |
172 | base_func = args.base | |
173 | attrs = args.attributes.split(',') | |
cb8f6aff L |
174 | if args.functions: |
175 | funcs = args.functions.split(',') | |
176 | if base_func and not base_func in funcs: | |
177 | print('Baseline function (%s) not found.' % base_func) | |
178 | sys.exit(os.EX_DATAERR) | |
179 | else: | |
180 | funcs = None | |
06b1de23 SP |
181 | |
182 | results = parse_file(args.input, args.schema) | |
cb8f6aff | 183 | process_results(results, attrs, funcs, base_func, args.graph, args.no_diff, args.no_header) |
a650b05e | 184 | return os.EX_OK |
25d52472 SP |
185 | |
186 | ||
187 | if __name__ == '__main__': | |
06b1de23 SP |
188 | parser = argparse.ArgumentParser() |
189 | ||
190 | # The required arguments. | |
191 | req = parser.add_argument_group(title='required arguments') | |
192 | req.add_argument('-a', '--attributes', required=True, | |
193 | help='Comma separated list of benchmark attributes.') | |
194 | req.add_argument('-i', '--input', required=True, | |
195 | help='Input JSON benchmark result file.') | |
196 | req.add_argument('-s', '--schema', required=True, | |
197 | help='Schema file to validate the result file.') | |
198 | ||
199 | # Optional arguments. | |
cb8f6aff L |
200 | parser.add_argument('-f', '--functions', |
201 | help='Comma separated list of functions.') | |
06b1de23 SP |
202 | parser.add_argument('-b', '--base', |
203 | help='IFUNC variant to set as baseline.') | |
140647ea SP |
204 | parser.add_argument('-g', '--graph', action='store_true', |
205 | help='Generate a graph from results.') | |
195abbf4 LS |
206 | parser.add_argument('--no-diff', action='store_true', |
207 | help='Do not print the difference from baseline.') | |
208 | parser.add_argument('--no-header', action='store_true', | |
209 | help='Do not print the header.') | |
06b1de23 SP |
210 | |
211 | args = parser.parse_args() | |
a650b05e | 212 | sys.exit(main(args)) |