Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 58 additions & 11 deletions benchmarks/dynamic-benchmarks/plot_adaptive_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import csv
import math
from collections import defaultdict
from statistics import median
from dataclasses import dataclass
from pathlib import Path

Expand All @@ -15,6 +16,7 @@
RUNS_CSV = RESULTS / "adaptive-image.csv"
SUMMARY_CSV = RESULTS / "adaptive-image-summary.csv"
FIGURE = RESULTS / "adaptive-image.pdf"
TRACE_TXT = RESULTS / "adaptive-image-trace.txt"

MODEL_ORDER = (
"polymerpim-jit",
Expand All @@ -34,6 +36,9 @@ class Summary:
stddev_ms: float
min_ms: float
max_ms: float
max_mean_ms: float
max_min_ms: float
max_max_ms: float
wall_mean_s: float
wall_stddev_s: float
wall_min_s: float
Expand Down Expand Up @@ -82,6 +87,28 @@ def load_rows():
return rows


def load_traces():
# Per-iteration times from the focused 64-DPU runs; absent is fine.
collected = defaultdict(list)
if not TRACE_TXT.is_file():
return {}
for line in TRACE_TXT.read_text().splitlines():
fields = line.split()
if len(fields) < 4 or not fields[3].startswith("iteration_ms="):
continue
series = [float(value) for value
in fields[3].split("=", 1)[1].split(";") if value]
if series:
collected[(f"polymerpim-{fields[0]}", int(fields[1]))].append(series)
# Median per iteration index, so one unlucky trial cannot set the shape.
traces = {}
for key, trials in collected.items():
width = min(len(series) for series in trials)
traces[key] = [median(series[index] for series in trials)
for index in range(width)]
return traces


def summarize(rows):
grouped = defaultdict(list)
for row in rows:
Expand All @@ -91,6 +118,7 @@ def summarize(rows):
summaries = []
for (model, elements_per_dpu, dpus), trials in sorted(grouped.items()):
mean_ms, stddev_ms = pooled_stats(trials)
trial_maxima = [float(row["max"]) for row in trials]
wall = [float(row["real_s"]) for row in trials if row["real_s"]]
summaries.append(Summary(
model=model,
Expand All @@ -103,6 +131,9 @@ def summarize(rows):
stddev_ms=stddev_ms,
min_ms=min(float(row["min"]) for row in trials),
max_ms=max(float(row["max"]) for row in trials),
max_mean_ms=sum(trial_maxima) / len(trial_maxima),
max_min_ms=min(trial_maxima),
max_max_ms=max(trial_maxima),
wall_mean_s=sum(wall) / len(wall),
wall_stddev_s=sample_stddev(wall),
wall_min_s=min(wall),
Expand Down Expand Up @@ -134,23 +165,39 @@ def plot(rows):
if len(sizes) != 2:
raise SystemExit("adaptive_image plot expects exactly two problem sizes")

figure, axes = plt.subplots(3, len(sizes), figsize=(10, 9.5), sharex="col")
traces = load_traces()
rows_count = 3 if traces else 2
figure, axes = plt.subplots(rows_count, len(sizes),
figsize=(10, 3.2 * rows_count))
for column, size in enumerate(sizes):
selected = [row for row in rows if row.elements_per_dpu == size]
worst_axis, mean_axis, wall_axis = axes[:, column]
column_axes = list(axes[:, column])
trace_axis = column_axes.pop(0) if traces else None
mean_axis, worst_axis = column_axes
for model in MODEL_ORDER:
draw(mean_axis, selected, model, "mean_ms")
draw(worst_axis, selected, model, "max_ms")
draw(wall_axis, selected, model,
"wall_mean_s", "wall_min_s", "wall_max_s")

worst_axis.set_title(f"{size:,} elements/DPU", fontweight="bold")
draw(worst_axis, selected, model,
"max_mean_ms", "max_min_ms", "max_max_ms")
if trace_axis is not None:
draw_series(trace_axis, model,
list(enumerate(traces.get((model, size), []),
start=1)),
linewidth=1.6)

top_axis = trace_axis if trace_axis is not None else mean_axis
top_axis.set_title(f"{size:,} elements/DPU", fontweight="bold")
mean_axis.set_ylabel("Mean iteration (ms)" if column == 0 else "")
worst_axis.set_ylabel("Worst iteration (ms)" if column == 0 else "")
wall_axis.set_ylabel(
"Process wall time: mean, min–max (s)" if column == 0 else "")
worst_axis.set_ylabel(
"Worst iteration: mean, min-max (ms)" if column == 0 else "")
if trace_axis is not None:
trace_axis.set_ylabel(
"Iteration time, 64 DPUs (ms)" if column == 0 else "")
trace_axis.set_xlabel("Iteration")
trace_axis.grid(True, which="major", color="#d8d8d8",
linewidth=0.8)
trace_axis.set_axisbelow(True)
dpus = sorted({row.dpus for row in selected})
for axis in (mean_axis, worst_axis, wall_axis):
for axis in (mean_axis, worst_axis):
configure_axis(axis, dpus, dpus, "DPUs")

figure.suptitle("Adaptive image: dynamic execution trade-offs",
Expand Down
10 changes: 10 additions & 0 deletions benchmarks/dynamic-benchmarks/polymerpim/adaptive_image/run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ int main() {
BenchTimer timer;
bench_stats_init(&stats);
uint32_t fine_updates = 0;
std::vector<double> iteration_ms;
iteration_ms.reserve(iterations);
std::vector<T> last_errors(channels, 0);
RuntimeStatistics runtime_before = statistics();

Expand Down Expand Up @@ -115,8 +117,16 @@ int main() {

bench_stop(&timer, 0);
bench_stats_update(&stats, timer.time[0]);
iteration_ms.push_back(timer.time[0] / 1000.0);
}

// Per-iteration trace: check_interval spikes dominate min/max.
std::cout << "iteration_ms=";
for (size_t i = 0; i < iteration_ms.size(); ++i) {
std::cout << (i != 0 ? ";" : "") << iteration_ms[i];
}
std::cout << std::endl;

std::vector<T> expected;
std::vector<T> target;
if (check_correctness) {
Expand Down
Loading