@@ -82,20 +82,84 @@ def __call__(self, results):
82
82
83
83
84
84
class dump_markdown_results_table :
85
- """Print a Markdown-formatted table displaying benchmark results
85
+ """Print Markdown-formatted tables displaying benchmark results
86
+
87
+ For each metric, this visualization prints out a table of benchmarks,
88
+ showing the value of the metric for each variant.
86
89
87
90
The 'out_file' key is mandatory; specify '-' to print to stdout.
88
91
92
+ 'extra_colums' can be an empty dict. The sample configuration below assumes
93
+ that each benchmark result has a 'success' and 'runtime' metric for both
94
+ variants, 'variant_1' and 'variant_2'. It adds a 'ratio' column to the table
95
+ for the 'runtime' metric, and a 'change' column to the table for the
96
+ 'success' metric. The 'text' lambda is called once for each benchmark. The
97
+ 'text' lambda accepts a single argument---a dict---that maps variant
98
+ names to the value of that variant for a particular metric. The lambda
99
+ returns a string that is rendered in the benchmark's row in the new column.
100
+ This allows you to emit arbitrary text or markdown formatting in response to
101
+ particular combinations of values for different variants, such as
102
+ regressions or performance improvements.
103
+
89
104
Sample configuration:
90
105
106
+ ```
91
107
visualize:
92
108
- type: dump_markdown_results_table
93
- out_file: '-'
109
+ out_file: "-"
110
+ extra_columns:
111
+ runtime:
112
+ - column_name: ratio
113
+ text: >
114
+ lambda b: str(b["variant_2"]/b["variant_1"])
115
+ if b["variant_2"] < (1.5 * b["variant_1"])
116
+ else "**" + str(b["variant_2"]/b["variant_1"])
117
+ success:
118
+ - column_name: change
119
+ text: >
120
+ lambda b: "" if b["variant_2"] == b["variant_1"]
121
+ else "newly passing" if b["variant_2"]
122
+ else "regressed"
123
+ ```
124
+
125
+ Example output:
126
+
127
+ ```
128
+ ## runtime
129
+
130
+ | Benchmark | variant_1 | variant_2 | ratio |
131
+ | --- | --- | --- | --- |
132
+ | bench_1 | 5 | 10 | **2.0** |
133
+ | bench_2 | 10 | 5 | 0.5 |
134
+
135
+ ## success
136
+
137
+ | Benchmark | variant_1 | variant_2 | notes |
138
+ | --- | --- | --- | --- |
139
+ | bench_1 | True | True | |
140
+ | bench_2 | True | False | regressed |
141
+ | bench_3 | False | True | newly passing |
142
+ ```
94
143
"""
95
144
96
145
97
- def __init__ (self , out_file ):
146
+ def __init__ (self , out_file , extra_columns = None ):
98
147
self .get_out_file = benchcomp .Outfile (out_file )
148
+ self .extra_columns = self ._eval_column_text (extra_columns or {})
149
+
150
+
151
+ @staticmethod
152
+ def _eval_column_text (column_spec ):
153
+ for columns in column_spec .values ():
154
+ for column in columns :
155
+ try :
156
+ column ["text" ] = eval (column ["text" ])
157
+ except SyntaxError :
158
+ logging .error (
159
+ "This column text is not a valid python program: '%s'" ,
160
+ column ["text" ])
161
+ sys .exit (1 )
162
+ return column_spec
99
163
100
164
101
165
@staticmethod
@@ -104,10 +168,10 @@ def _get_template():
104
168
{% for metric, benchmarks in d["metrics"].items() %}
105
169
## {{ metric }}
106
170
107
- | Benchmark | {% for variant in d["variants"] %} {{ variant }} |{% endfor %}
108
- | --- | {% for variant in d["variants"] %}--- |{% endfor -%}
171
+ | Benchmark | {% for variant in d["variants"][metric] %} {{ variant }} |{% endfor %}
172
+ | --- |{% for variant in d["variants"][metric] %} --- |{% endfor -%}
109
173
{% for bench_name, bench_variants in benchmarks.items () %}
110
- | {{ bench_name }} {% for variant in d["variants"] -%}
174
+ | {{ bench_name }} {% for variant in d["variants"][metric] -%}
111
175
| {{ bench_variants[variant] }} {% endfor %}|
112
176
{%- endfor %}
113
177
{% endfor -%}
@@ -134,10 +198,35 @@ def _organize_results_into_metrics(results):
134
198
return ret
135
199
136
200
201
+ def _add_extra_columns (self , metrics ):
202
+ for metric , benches in metrics .items ():
203
+ try :
204
+ columns = self .extra_columns [metric ]
205
+ except KeyError :
206
+ continue
207
+ for bench , variants in benches .items ():
208
+ tmp_variants = dict (variants )
209
+ for column in columns :
210
+ variants [column ["column_name" ]] = column ["text" ](tmp_variants )
211
+
212
+
213
+ @staticmethod
214
+ def _get_variants (metrics ):
215
+ ret = {}
216
+ for metric , benches in metrics .items ():
217
+ for bench , variants in benches .items ():
218
+ ret [metric ] = list (variants .keys ())
219
+ break
220
+ return ret
221
+
222
+
137
223
def __call__ (self , results ):
224
+ metrics = self ._organize_results_into_metrics (results )
225
+ self ._add_extra_columns (metrics )
226
+
138
227
data = {
139
- "metrics" : self . _organize_results_into_metrics ( results ) ,
140
- "variants" : list ( results [ "benchmarks" ]. values ())[ 0 ][ "variants" ] ,
228
+ "metrics" : metrics ,
229
+ "variants" : self . _get_variants ( metrics ) ,
141
230
}
142
231
143
232
env = jinja2 .Environment (
0 commit comments