8 from pathlib
import Path
11 from bokeh.layouts
import column
12 from bokeh.models
import ColumnDataSource, Whisker
13 from bokeh.plotting
import figure, show
14 from bokeh.transform
import factor_cmap, jitter
16 TimeNS = typing.NewType(
"TimeNS", int)
20 """Variance informations"""
37 return f
"<Variance {self.mean=}, {self.median=}, {self.stddev=}>"
43 def __init__(self, name: str, times: list[TimeNS], variance:
None | Variance):
50 return Data(name, [],
None)
53 return f
"<Data {self.name=}, {self.times=}, {self.variance=}>"
57 """Convert JSON output time into TimeNS"""
58 t = float(bench[
"real_time"])
59 if bench[
"time_unit"] ==
"ns":
61 elif bench[
"time_unit"] ==
"us":
62 return TimeNS(int(t * 1e3))
63 elif bench[
"time_unit"] ==
"ms":
64 return TimeNS(int(t * 1e6))
65 elif bench[
"time_unit"] ==
"s":
66 return TimeNS(int(t * 1e9))
70 """Parse google benchmark JSON output"""
73 for bench
in json_content[
"benchmarks"]:
74 run_name = bench[
"run_name"]
75 run_type = bench[
"run_type"]
76 if run_type ==
"aggregate":
77 aggregate_name = bench[
"aggregate_name"]
78 variance = variance_dict.setdefault(run_name, Variance.default())
79 if aggregate_name ==
"mean":
81 elif aggregate_name ==
"median":
83 elif aggregate_name ==
"stddev":
85 elif run_type ==
"iteration":
86 data = data_dict.setdefault(run_name, Data.from_name(run_name))
89 for k, v
in variance_dict.items():
90 data_dict[k].variance = v
92 return list(data_dict.values())
96 """Convert data name into a nice label"""
97 return s.split(
"/")[1]
101 """Compute data lower and upper bounds to display Whisker box"""
104 return (float(v.median - v.stddev), float(v.median + v.stddev))
106 return (float(
"nan"), float(
"nan"))
110 """Return a list with data name and time"""
111 return [(
class_name(data.name), t)
for t
in data.times]
120 lower = lower_upper[:, 0]
121 upper = lower_upper[:, 1]
125 sizing_mode=
"stretch_width",
127 background_fill_color=
"#efefef",
128 title=
"Benchmark results",
130 p.xgrid.grid_line_color =
None
133 whisker_source = ColumnDataSource(data=dict(base=classes, upper=upper, lower=lower))
138 source=whisker_source,
142 error.upper_head.size = 20
143 error.lower_head.size = 20
147 flat_data = np.array(
148 list(itertools.chain.from_iterable([
flatten_data(d)
for d
in datas]))
151 scatter_source = ColumnDataSource(
152 data=dict(cl=flat_data[:, 0], time=flat_data[:, 1].astype(int))
156 jitter(
"cl", 0.3, range=p.x_range),
158 source=scatter_source,
162 color=factor_cmap(
"cl",
"Light7", classes),
170 for data_batch
in itertools.batched(datas, 7):
175 sizing_mode=
"stretch_width",
183 raise argparse.ArgumentTypeError(f
"{file} is not a file")
188 parser = argparse.ArgumentParser(description=
"Plot benchmark results")
189 parser.add_argument(
"json_output", help=
"Include directory", type=is_file)
195 args = parser.parse_args(args)
201 if __name__ ==
"__main__":