import argparse
import os
import matplotlib.pyplot as plt
import numpy as np
parser = argparse.ArgumentParser()
parser.add_argument("file")
parser.add_argument("--output-dir", default=".", help="Directory for output SVG files")
parser.add_argument(
"--read-word",
default="u32",
choices=["u16", "u32", "u64"],
help="Read word size for plot titles (default: u32)",
)
parser.add_argument(
"--write-word",
default="u64",
choices=["u16", "u32", "u64"],
help="Write word size for plot titles (default: u64)",
)
args = parser.parse_args()
SUBSCRIPTS = str.maketrans("0123456789", "₀₁₂₃₄₅₆₇₈₉")
NICE_LABELS = {
"unary": "Unary",
"gamma": "γ",
"delta": "δ",
"omega": "ω",
"vbytebe": "VByte(BE)",
"vbytele": "VByte(LE)",
"zeta3": "ζ₃ (tables)",
"pi2": "π₂ (tables)",
}
def nice_label(code):
if code in NICE_LABELS:
return NICE_LABELS[code]
for prefix, symbol in [("zeta_", "ζ"), ("pi_", "π")]:
if code.startswith(prefix):
return symbol + code[len(prefix) :].translate(SUBSCRIPTS)
for prefix, name in [
("rice_", "Rice"),
("exgol_", "ExpGolomb"),
("gol_", "Golomb"),
]:
if code.startswith(prefix):
return "%s(%s)" % (name, code[len(prefix) :])
return code
with open(args.file) as f:
data = f.read().splitlines()
header = [x.strip() for x in data[0].split("\t")]
data = [dict(zip(header, [x.strip() for x in line.split("\t")])) for line in data[1:]]
def create_plot(operations, title):
codes = list(set(d["code"] for d in operations))
code_performance = {}
for code in codes:
code_data = [d for d in operations if d["code"] == code]
best_mean = min(float(d["mean"]) for d in code_data)
code_performance[code] = best_mean
codes = sorted(codes, key=lambda x: code_performance[x])
fig, ax = plt.subplots(figsize=(15, 8))
x = np.arange(len(codes))
width = 0.35
little_endian = [
(d["mean"], d["cilower"], d["ciupper"])
for code in codes
for d in operations
if d["code"] == code and d["endian"] == "LE"
]
means_le, min_le, max_le = zip(*little_endian)
means_le = [float(x) for x in means_le]
min_le = [float(x) for x in min_le]
max_le = [float(x) for x in max_le]
yerr_le = np.array(
[
np.array(means_le) - np.array(min_le),
np.array(max_le) - np.array(means_le),
]
)
big_endian = [
(d["mean"], d["cilower"], d["ciupper"])
for code in codes
for d in operations
if d["code"] == code and d["endian"] == "BE"
]
means_be, min_be, max_be = zip(*big_endian)
means_be = [float(x) for x in means_be]
min_be = [float(x) for x in min_be]
max_be = [float(x) for x in max_be]
yerr_be = np.array(
[
np.array(means_be) - np.array(min_be),
np.array(max_be) - np.array(means_be),
]
)
ax.errorbar(
x - width / 2,
means_le,
yerr=yerr_le,
fmt=".",
label="Little Endian",
capsize=5,
capthick=1,
markersize=8,
)
ax.errorbar(
x + width / 2,
means_be,
yerr=yerr_be,
fmt=".",
label="Big Endian",
capsize=5,
capthick=1,
markersize=8,
)
for i, (mean_le, mean_be) in enumerate(zip(means_le, means_be)):
ax.text(
i - width / 2,
mean_le + (max_le[i] - min_le[i]) / 2 + 0.1,
f"{mean_le:.3f}",
rotation=90,
ha="center",
va="bottom",
)
ax.text(
i + width / 2,
mean_be + (max_be[i] - min_be[i]) / 2 + 0.1,
f"{mean_be:.3f}",
rotation=90,
ha="center",
va="bottom",
)
ax.set_ylabel("Time (ns)")
ax.set_title(f"{title}")
ax.set_xticks(x)
ax.set_xticklabels([nice_label(c) for c in codes], rotation=45, ha="right")
ax.legend()
ax.set_axisbelow(True)
ax.yaxis.grid(True, color="gray", linestyle="--", alpha=0.2)
y_min, y_max = ax.get_ylim()
ax.set_ylim(y_min, y_max * 1.1)
plt.tight_layout()
return fig
for op_val, dist_val, word, dist_label, filename in [
(
"read",
"implied",
args.read_word,
"implied distribution",
"read_implied_performance.svg",
),
(
"write",
"implied",
args.write_word,
"implied distribution",
"write_implied_performance.svg",
),
(
"read",
"univ",
args.read_word,
"universal Zipf distribution ≈1/x (first billion integers)",
"read_univ_performance.svg",
),
(
"write",
"univ",
args.write_word,
"universal Zipf distribution ≈1/x (first billion integers)",
"write_univ_performance.svg",
),
]:
title = "%s (%s %s word) on %s" % (op_val.capitalize(), word, op_val, dist_label)
ops = [d for d in data if d["op"] == op_val and d["dist"] == dist_val]
if not ops:
continue
fig = create_plot(ops, title)
fig.savefig(os.path.join(args.output_dir, filename), dpi=300, bbox_inches="tight")
plt.close("all")