import os
import pathlib
import subprocess
import sys
ROOT = pathlib.Path(__file__).resolve().parent.parent
WORKLOADS = [
("contig-write", 5),
("contig-read", 5),
("contig-view", 5),
("into-read", 5),
("into-slice", 5),
("chunked-write", 5),
("chunked-read", 5),
("chunked-into-read", 5),
("chunked-into-slice", 5),
("deflate-write", 3),
("deflate-read", 5),
("deflate-slice", 5),
("slice-read", 5),
("small-write", 3),
("small-read", 5),
("attr-write", 3),
("append", 5),
]
def build(prefix):
subprocess.run(
["cargo", "build", "--release", "--bin", "perf_probe"],
cwd=ROOT,
check=True,
)
subprocess.run(
[
"cargo",
"build",
"--release",
"--bin",
"perf_probe",
"--features",
"parallel",
"--target-dir",
str(ROOT / "target" / "parallel"),
],
cwd=ROOT,
check=True,
)
subprocess.run(
[
"cargo",
"build",
"--release",
"--bin",
"perf_probe",
"--features",
"mmap",
"--target-dir",
str(ROOT / "target" / "mmap"),
],
cwd=ROOT,
check=True,
)
c_bin = ROOT / "target" / "release" / "perf_probe_c"
subprocess.run(
[
"cc",
"-O2",
str(ROOT / "perf" / "probe.c"),
f"-I{prefix}/include",
f"-L{prefix}/lib",
f"-Wl,-rpath,{prefix}/lib",
"-lhdf5",
"-o",
str(c_bin),
],
check=True,
)
return (
ROOT / "target" / "release" / "perf_probe",
ROOT / "target" / "parallel" / "release" / "perf_probe",
ROOT / "target" / "mmap" / "release" / "perf_probe",
c_bin,
)
def run_probe(binary, workdir, workload, reps):
out = subprocess.run(
[str(binary), str(workdir), workload, str(reps)],
capture_output=True,
text=True,
check=True,
).stdout
ns = [
int(line.split()[-1])
for line in out.splitlines()
if line.startswith(f"BENCH {workload} ")
]
if len(ns) != reps:
raise RuntimeError(f"{binary} {workload}: expected {reps} reps, got {ns}")
return ns
def main():
prefix = os.environ.get(
"RUST_HDF5_PERF_PREFIX",
str(pathlib.Path.home() / "micromamba" / "envs" / "tomo"),
)
workdir = pathlib.Path(
os.environ.get("RUST_HDF5_PERF_WORKDIR", "/dev/shm/rust-hdf5-perf")
)
workdir.mkdir(parents=True, exist_ok=True)
only = os.environ.get("RUST_HDF5_PERF_ONLY")
matrix = [
(w, r) for w, r in WORKLOADS if only is None or w in only.split(",")
]
rust_bin, par_bin, mmap_bin, c_bin = build(prefix)
print(f"{'workload':<14} {'C min ms':>10} {'rust min ms':>12} "
f"{'ratio':>7} {'par min ms':>11} {'par':>7} "
f"{'mmap min ms':>12} {'mmap':>7}")
for workload, reps in matrix:
per_round = max(2, (reps + 1) // 2)
c_ns, r_ns, p_ns, m_ns = [], [], [], []
for _ in range(2):
c_ns += run_probe(c_bin, workdir, workload, per_round)
r_ns += run_probe(rust_bin, workdir, workload, per_round)
p_ns += run_probe(par_bin, workdir, workload, per_round)
m_ns += run_probe(mmap_bin, workdir, workload, per_round)
cmin, rmin = min(c_ns) / 1e6, min(r_ns) / 1e6
pmin, mmin = min(p_ns) / 1e6, min(m_ns) / 1e6
print(f"{workload:<14} {cmin:>10.2f} {rmin:>12.2f} "
f"{rmin / cmin:>6.2f}x {pmin:>11.2f} {pmin / cmin:>6.2f}x "
f"{mmin:>12.2f} {mmin / cmin:>6.2f}x")
sys.stdout.flush()
for leftover in workdir.iterdir():
leftover.unlink()
if __name__ == "__main__":
main()