Skip to content

Commit 9df9c10

Browse files
committed
push
1 parent 6625c29 commit 9df9c10

File tree

3 files changed

+585
-0
lines changed

3 files changed

+585
-0
lines changed

script/plot_real_device_suite.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import csv
5+
from pathlib import Path
6+
7+
import matplotlib.pyplot as plt
8+
import numpy as np
9+
10+
11+
LABELS = {
12+
"gromacs": "GROMACS",
13+
"tigon_tpcc": "Tigon TPCC",
14+
"mlc_idle": "MLC idle",
15+
"wrf_short": "WRF-short",
16+
"gapbs_bfs": "GAPBS BFS",
17+
"gapbs_pr": "GAPBS PR",
18+
"gapbs_cc": "GAPBS CC",
19+
"gapbs_sssp": "GAPBS SSSP",
20+
}
21+
22+
23+
def load_records(summary_csv: Path) -> list[dict[str, str]]:
24+
with summary_csv.open() as fh:
25+
return list(csv.DictReader(fh))
26+
27+
28+
def compute_normalized(records: list[dict[str, str]], baseline_node: int, compare_node: int) -> list[dict[str, object]]:
29+
by_workload: dict[str, dict[int, dict[str, str]]] = {}
30+
for record in records:
31+
if record["status"] != "ok":
32+
continue
33+
workload = record["workload"]
34+
node = int(record["node"])
35+
by_workload.setdefault(workload, {})[node] = record
36+
37+
normalized: list[dict[str, object]] = []
38+
for workload, nodes in by_workload.items():
39+
if baseline_node not in nodes or compare_node not in nodes:
40+
continue
41+
baseline = float(nodes[baseline_node]["metric_value"])
42+
compare = float(nodes[compare_node]["metric_value"])
43+
higher_is_better = nodes[baseline_node]["higher_is_better"].lower() == "true"
44+
ratio = compare / baseline if higher_is_better else baseline / compare
45+
normalized.append(
46+
{
47+
"workload": workload,
48+
"label": LABELS.get(workload, workload),
49+
"ratio": ratio,
50+
"baseline": baseline,
51+
"compare": compare,
52+
"unit": nodes[baseline_node]["unit"],
53+
"higher_is_better": higher_is_better,
54+
}
55+
)
56+
return normalized
57+
58+
59+
def plot(normalized: list[dict[str, object]], output_dir: Path, baseline_node: int, compare_node: int) -> None:
60+
output_dir.mkdir(parents=True, exist_ok=True)
61+
labels = [entry["label"] for entry in normalized]
62+
ratios = np.array([entry["ratio"] for entry in normalized], dtype=float)
63+
colors = ["#0b6e4f" if ratio >= 1.0 else "#c84c09" for ratio in ratios]
64+
65+
fig, ax = plt.subplots(figsize=(8, 4), constrained_layout=True)
66+
bars = ax.bar(labels, ratios, color=colors, width=0.68)
67+
ax.axhline(1.0, color="#222222", linewidth=1.2, linestyle="--")
68+
ax.set_ylabel(f"Normalized performance (node{compare_node} vs node{baseline_node})")
69+
ax.set_title("Real-device benchmark comparison")
70+
ax.set_ylim(0, max(1.4, ratios.max() * 1.18))
71+
ax.tick_params(axis="x", rotation=20)
72+
73+
for bar, entry in zip(bars, normalized, strict=True):
74+
height = bar.get_height()
75+
ax.text(
76+
bar.get_x() + bar.get_width() / 2.0,
77+
height + 0.02,
78+
f"{height:.2f}x",
79+
ha="center",
80+
va="bottom",
81+
fontsize=9,
82+
)
83+
raw = f"n{baseline_node}={entry['baseline']:.1f} {entry['unit']}\n" f"n{compare_node}={entry['compare']:.1f} {entry['unit']}"
84+
ax.text(
85+
bar.get_x() + bar.get_width() / 2.0,
86+
0.03,
87+
raw,
88+
ha="center",
89+
va="bottom",
90+
fontsize=7,
91+
color="#333333",
92+
rotation=90,
93+
)
94+
95+
stem = output_dir / "real_device_normalized_performance"
96+
fig.savefig(stem.with_suffix(".png"), dpi=220)
97+
fig.savefig(stem.with_suffix(".pdf"))
98+
99+
100+
def parse_args() -> argparse.Namespace:
101+
parser = argparse.ArgumentParser(description="Plot normalized real-device benchmark results")
102+
parser.add_argument("summary_csv", type=Path, help="Path to summary.csv from run_real_device_suite.py")
103+
parser.add_argument("--baseline-node", type=int, default=1, help="Baseline node for normalization")
104+
parser.add_argument("--compare-node", type=int, default=2, help="Compared node for normalization")
105+
parser.add_argument("--out-dir", type=Path, default=None, help="Figure output directory")
106+
return parser.parse_args()
107+
108+
109+
def main() -> int:
110+
args = parse_args()
111+
records = load_records(args.summary_csv)
112+
normalized = compute_normalized(records, args.baseline_node, args.compare_node)
113+
if not normalized:
114+
raise SystemExit("No comparable successful records found in summary.csv")
115+
out_dir = args.out_dir or args.summary_csv.parent / "figures"
116+
plot(normalized, out_dir, args.baseline_node, args.compare_node)
117+
return 0
118+
119+
120+
if __name__ == "__main__":
121+
raise SystemExit(main())

0 commit comments

Comments
 (0)