|
1 | 1 | import json |
| 2 | +import math |
2 | 3 | import statistics |
3 | 4 |
|
4 | 5 | import plotly.graph_objects as go |
5 | 6 | import plotly.io as io |
| 7 | +from plotly.subplots import make_subplots |
6 | 8 |
|
7 | 9 | # This script parses JMH benchmarking results into charts developed using plot.ly (https://plotly.com/) |
8 | 10 | # It expects JMH benchmark results be dumped to a file "scijava-ops-benchmark_results.json", within its directory. |
|
17 | 19 | "name": "BenchmarkMatching", |
18 | 20 | "title": "Basic Op Matching Performance", |
19 | 21 | "bars": { |
20 | | - "noOps": "Static Method", |
21 | | - "noOpsAdapted": f"Static Method {A}", |
| 22 | + "sjOpsAdapted": f"SciJava Ops {A}", |
22 | 23 | "sjOps": "SciJava Ops", |
23 | | - "sjOpsAdapted": f"SciJava Ops {A}" |
| 24 | + "noOpsAdapted": f"Static Method {A}", |
| 25 | + "noOps": "Static Method", |
24 | 26 | } |
25 | 27 | }, |
26 | 28 | { |
27 | 29 | "name": "BenchmarkCaching", |
28 | 30 | "title": "Caching Effects on Op Matching Performance", |
29 | 31 | "bars": { |
30 | | - "noOps": "Static Method", |
| 32 | + "sjOpsWithCache": "SciJava Ops (cached)", |
31 | 33 | "sjOps": "SciJava Ops", |
32 | | - "sjOpsWithCache": "SciJava Ops (cached)" |
| 34 | + "noOps": "Static Method", |
33 | 35 | } |
34 | 36 | }, |
35 | 37 | { |
36 | 38 | "name": "BenchmarkConversion", |
37 | 39 | "title": "Parameter Conversion Performance", |
38 | 40 | "bars": { |
39 | | - "noOpsConverted": f"Static Method {C}", |
40 | | - "noOpsAdaptedAndConverted": f"Static Method {AC}", |
| 41 | + "sjOpsConvertedAndAdapted": f"SciJava Ops {AC}", |
41 | 42 | "sjOpsConverted": f"SciJava Ops {C}", |
42 | | - "sjOpsConvertedAndAdapted": f"SciJava Ops {AC}" |
| 43 | + "noOpsAdaptedAndConverted": f"Static Method {AC}", |
| 44 | + "noOpsConverted": f"Static Method {C}", |
43 | 45 | } |
44 | 46 | }, |
45 | 47 | { |
46 | 48 | "name": "BenchmarkFrameworks", |
47 | 49 | "title": "Algorithm Execution Performance by Framework", |
48 | 50 | "bars": { |
49 | | - "noOps": "Static Method", |
| 51 | + "ijOps": "ImageJ Ops", |
50 | 52 | "sjOps": "SciJava Ops", |
51 | | - "ijOps": "ImageJ Ops" |
| 53 | + "noOps": "Static Method", |
52 | 54 | } |
53 | 55 | }, |
54 | 56 | { |
55 | 57 | "name": "BenchmarkCombined", |
56 | 58 | "title": "Combined Performance Metrics", |
57 | 59 | "bars": { |
58 | | - "noOps": "Static Method", |
59 | | - "noOpsAdapted": f"Static Method {A}", |
60 | | - "noOpsConverted": f"Static Method {C}", |
61 | | - "noOpsAdaptedAndConverted": f"Static Method {AC}", |
62 | | - "sjOpsWithCache": "SciJava Ops (cached)", |
63 | | - "sjOps": "SciJava Ops", |
64 | | - "sjOpsAdapted": f"SciJava Ops {A}", |
65 | | - "sjOpsConverted": f"SciJava Ops {C}", |
66 | | - "sjOpsConvertedAndAdapted": f"SciJava Ops {AC}", |
67 | 60 | "ijOps": "ImageJ Ops", |
| 61 | + "sjOpsConvertedAndAdapted": f"SciJava Ops {AC}", |
| 62 | + "sjOpsConverted": f"SciJava Ops {C}", |
| 63 | + "sjOpsAdapted": f"SciJava Ops {A}", |
| 64 | + "sjOps": "SciJava Ops", |
| 65 | + "sjOpsWithCache": "SciJava Ops (cached)", |
| 66 | + "noOpsAdaptedAndConverted": f"Static Method {AC}", |
| 67 | + "noOpsConverted": f"Static Method {C}", |
| 68 | + "noOpsAdapted": f"Static Method {A}", |
| 69 | + "noOps": "Static Method", |
68 | 70 | } |
69 | 71 | } |
70 | 72 | ] |
|
87 | 89 | name = figure["name"] |
88 | 90 | print(f"Generating figure for {name}", end="") |
89 | 91 |
|
90 | | - x = [] |
91 | | - y = [] |
92 | | - error_y = [] |
93 | | - error_y_minus = [] |
| 92 | + labels = [] |
| 93 | + values = [] |
| 94 | + errors = [] |
94 | 95 |
|
95 | 96 | # Add each benchmark in the class |
96 | 97 | for test, label in figure["bars"].items(): |
97 | | - print(f".", end="") |
| 98 | + print(".", end="") |
98 | 99 | result = results[test] |
99 | | - x.append(label) |
100 | | - y.append(result["score"]) |
101 | | - error_y.append(result["minmax"][1] - result["score"]) |
102 | | - error_y_minus.append(result["score"] - result["minmax"][0]) |
103 | | - |
104 | | - # Create a bar chart |
105 | | - fig = go.Figure() |
106 | | - fig.add_bar( |
107 | | - x=x, |
108 | | - y=y, |
109 | | - error_y=dict(type='data', array=error_y, arrayminus=error_y_minus), |
| 100 | + labels.append(label) |
| 101 | + score = result["score"] |
| 102 | + values.append(score) |
| 103 | + error = [result["minmax"][1] - score, score - result["minmax"][0]] |
| 104 | + errors.append(error) |
| 105 | + |
| 106 | + # Create a subplot with shared y-axis |
| 107 | + fig = make_subplots(rows=1, cols=2, shared_yaxes=True, horizontal_spacing=0.02) |
| 108 | + |
| 109 | + # Add log scale bars (left side) |
| 110 | + fig.add_trace( |
| 111 | + go.Bar( |
| 112 | + y=labels, |
| 113 | + x=values, |
| 114 | + orientation='h', |
| 115 | + error_x=dict(type='data', symmetric=False, array=[e[0] for e in errors], arrayminus=[e[1] for e in errors]), |
| 116 | + name="Log Scale", |
| 117 | + marker_color='blue' |
| 118 | + ), |
| 119 | + row=1, col=1 |
110 | 120 | ) |
| 121 | + |
| 122 | + # Add linear scale bars (right side) |
| 123 | + fig.add_trace( |
| 124 | + go.Bar( |
| 125 | + y=labels, |
| 126 | + x=values, |
| 127 | + orientation='h', |
| 128 | + error_x=dict(type='data', symmetric=False, array=[e[0] for e in errors], arrayminus=[e[1] for e in errors]), |
| 129 | + name="Linear Scale", |
| 130 | + marker_color='red' |
| 131 | + ), |
| 132 | + row=1, col=2 |
| 133 | + ) |
| 134 | + |
| 135 | + # Update layout |
111 | 136 | fig.update_layout( |
112 | 137 | title_text=figure["title"] + f"<br><sup style=\"color: gray\">{A}=Adaptation, {C}=Conversion, {AC}=Adaptation & Conversion</sup>", |
113 | | - yaxis_title="<b>Performance (μs/execution)</b>" |
| 138 | + barmode='relative', |
| 139 | + yaxis=dict(title=""), |
| 140 | + #xaxis=dict(title="Log Scale (μs/execution)", type="log"), |
| 141 | + xaxis=dict( |
| 142 | + title="Log Scale (μs/execution)", |
| 143 | + type="log", |
| 144 | + range=[min(values), max(values)], |
| 145 | + ), |
| 146 | + xaxis2=dict(title="Linear Scale (μs/execution)"), |
| 147 | + height=max(500, 50 * len(labels)), # Adjust height based on number of bars |
| 148 | + showlegend=False |
114 | 149 | ) |
115 | 150 |
|
| 151 | + # Add a vertical line at x=0 |
| 152 | + fig.add_vline(x=0, line_width=2, line_color="black") |
| 153 | + |
| 154 | + # Reverse the log scale axis |
| 155 | + fig.update_xaxes(autorange="reversed", row=1, col=1) |
| 156 | + |
116 | 157 | # Convert to JSON and dump |
117 | 158 | with open(f"images/{name}.json", "w") as f: |
118 | 159 | f.write(io.to_json(fig)) |
|
0 commit comments