@@ -82,20 +82,84 @@ def __call__(self, results):
8282
8383
8484class 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.
8689
8790 The 'out_file' key is mandatory; specify '-' to print to stdout.
8891
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+
89104 Sample configuration:
90105
106+ ```
91107 visualize:
92108 - 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+ ```
94143 """
95144
96145
97- def __init__ (self , out_file ):
146+ def __init__ (self , out_file , extra_columns = None ):
98147 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
99163
100164
101165 @staticmethod
@@ -104,10 +168,10 @@ def _get_template():
104168 {% for metric, benchmarks in d["metrics"].items() %}
105169 ## {{ metric }}
106170
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 -%}
109173 {% 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] -%}
111175 | {{ bench_variants[variant] }} {% endfor %}|
112176 {%- endfor %}
113177 {% endfor -%}
@@ -134,10 +198,35 @@ def _organize_results_into_metrics(results):
134198 return ret
135199
136200
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+
137223 def __call__ (self , results ):
224+ metrics = self ._organize_results_into_metrics (results )
225+ self ._add_extra_columns (metrics )
226+
138227 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 ) ,
141230 }
142231
143232 env = jinja2 .Environment (
0 commit comments