import csv
import sys
from collections import defaultdict
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
csv_path = sys.argv[1] if len(sys.argv) > 1 else "pibench-sweep.csv"
out_path = sys.argv[2] if len(sys.argv) > 2 else "pibench-verify.png"
data = defaultdict(dict)
with open(csv_path) as f:
for row in csv.DictReader(f):
try:
data[row["backend"]][int(row["len"])] = (float(row["pps"]), float(row["cpu_us_frame"]))
except (ValueError, KeyError):
continue
PAIRS = [
("ring", "ring_verify", "#1b9e77", "ferranet ring"),
("pcap", "pcap_verify", "#e7298a", "libpcap"),
]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13, 5))
for plain, verify, color, label in PAIRS:
if plain not in data or verify not in data:
continue
lens = sorted(set(data[plain]) & set(data[verify]))
xs = [data[plain][l][0] / 1000 for l in lens] ax1.plot(xs, [data[plain][l][1] for l in lens],
marker="o", color=color, label=f"{label} (capture only)")
ax1.plot(xs, [data[verify][l][1] for l in lens],
marker="D", ls="--", color=color, label=f"{label} + etherparse")
ax2.plot(xs, [data[verify][l][1] - data[plain][l][1] for l in lens],
marker="s", color=color, label=label)
ax1.set(title="Per-frame CPU: capture vs capture + etherparse",
xlabel="received packets/sec (kpps)", ylabel="CPU µs / frame")
ax2.set(title="Isolated etherparse overhead (verify − plain)",
xlabel="received packets/sec (kpps)", ylabel="added CPU µs / frame")
for ax in (ax1, ax2):
ax.grid(True, alpha=0.3)
ax.legend()
ax.set_ylim(bottom=0)
fig.suptitle("etherparse parse + classify + IPv4-checksum cost per frame (Raspberry Pi Zero 2 W)",
fontsize=12)
fig.tight_layout(rect=(0, 0, 1, 0.95))
fig.savefig(out_path, dpi=120)
print(f"wrote {out_path}")