import argparse
import json
import platform
import re
import os
import subprocess
import sys
import tomllib
from datetime import datetime, timezone
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
SUMMARY_VERSION = "7"
DEFAULT_MESSAGES = 1000
MESSAGES = DEFAULT_MESSAGES
FLOOR_PER_DEFAULT_RUN = 100_000
FLOOR = FLOOR_PER_DEFAULT_RUN
COLD_FLOOR = 1_000
def configure(messages):
global MESSAGES, FLOOR
if messages <= 0:
sys.exit("--messages must be a positive number of deliveries")
MESSAGES = messages
FLOOR = FLOOR_PER_DEFAULT_RUN * messages // DEFAULT_MESSAGES
class Scenario:
def __init__(self, name, framework, gated=True, note=None):
self.name = name
self.framework = framework
self.gated = gated
self.note = note
COLD = "first"
COUNTS = ("base", "twice")
SCENARIOS = [
Scenario(
"consume, JSON decode into a small struct",
"consume_json/service",
),
Scenario(
"consume, JSON decode of a 1 KB body",
"consume_json_kilobyte/service",
),
Scenario(
"consume on the byte lane, no codec",
"consume_lane/service",
),
Scenario(
"consume through a pool of four workers",
"consume_pool/service",
),
Scenario(
"consume on four dedicated threads",
"consume_threads/service",
),
Scenario(
"consume through a middleware stack of one",
"middleware/service_one",
),
Scenario(
"consume through a middleware stack of four",
"middleware/service_four",
),
Scenario(
"consume in batches of 64",
"batch/service",
),
Scenario(
"reply, encoded to a declared destination",
"reply/service",
),
Scenario(
"reply to a transport that reads the payload",
"reply_lending/service",
),
Scenario(
"publish through an Out slot with one transform",
"out_slot/service",
),
Scenario(
"publish with a typed header contract",
"typed_headers_write/service",
),
Scenario(
"read a typed header contract, then publish",
"typed_headers_read/service",
),
Scenario(
"request and reply, one round trip",
"request_reply/service",
),
Scenario(
"a delivery that asks to be redelivered, natively by the broker",
"retry_copy/service",
gated=False,
note="cold path: measured and reported, never gated",
),
]
def metric(summary, tool, name):
for profile in summary["profiles"]:
if profile["tool"] != tool:
continue
values = profile["data"]["total"]["metrics"].get(name, {}).get("values", {})
new = values.get("new")
return None if new is None else int(new)
return None
def summaries(path):
found = []
for line in path.read_text().splitlines():
line = line.strip()
if not line:
continue
summary = json.loads(line)
version = summary.get("version")
if version != SUMMARY_VERSION:
sys.exit(
f"the benchmark summary has layout version {version}, and this script reads "
f"version {SUMMARY_VERSION}: read the new layout in `metric` and `breaches` "
"and raise SUMMARY_VERSION"
)
found.append(summary)
return found
def benchmark(summary):
return f"{Path(summary['benchmark_file']).stem}/{summary['function_name']}"
def measurements(runs):
found = {}
for summary in runs:
found[f"{benchmark(summary)}/{summary['id']}"] = {
"instructions": metric(summary, "Callgrind", "Ir"),
"allocations": metric(summary, "DHAT", "TotalBlocks"),
}
return found
METRIC_NAMES = {("Callgrind", "Ir"): "instructions", ("Dhat", "TotalBlocks"): "allocations"}
def run_name(run):
counts = {COLD: 1, COUNTS[0]: MESSAGES, COUNTS[1]: 2 * MESSAGES}
if run not in counts:
return run
return "one delivery" if counts[run] == 1 else f"{counts[run]} deliveries"
def as_text(value):
return str(value) if isinstance(value, int) else f"{value:g}"
def breach(regression, metrics):
[(kind, detail)] = regression.items()
[(tool, name)] = detail["metric"].items()
label = METRIC_NAMES.get((tool, name), f"{tool} {name}")
if kind == "Soft":
return (
f"{label} {as_text(detail['old'])} -> {as_text(detail['new'])}, "
f"{float(detail['diff_pct']):+.2f}% against a limit of +{float(detail['limit']):g}%"
)
old = metrics.get(name, {}).get("values", {}).get("old")
change = "" if old is None else f"{as_text(old)} -> "
return f"{label} {change}{as_text(detail['new'])} against a limit of {as_text(detail['limit'])}"
def breaches(runs):
names = {scenario.framework: scenario.name for scenario in SCENARIOS}
found = []
for summary in runs:
where = f"{names.get(benchmark(summary), benchmark(summary))}, {run_name(summary['id'])}"
for profile in summary["profiles"]:
total = profile["data"]["total"]
for regression in total["regressions"]:
found.append(f"{where}: {breach(regression, total['metrics'])}")
return found
def rounded(figure):
return round(figure, 3) if abs(figure) < 1 else round(figure, 1)
def command(*args):
return subprocess.run(args, capture_output=True, text=True, check=True).stdout.strip()
def read(path, default=None):
try:
return Path(path).read_text().strip()
except OSError:
return default
def proc_cpuinfo():
return read("/proc/cpuinfo", "") or ""
def cpu_model():
for line in proc_cpuinfo().splitlines():
if line.startswith("model name"):
return line.split(":", 1)[1].strip()
return platform.processor() or "unknown"
X86_LEVELS = [
("x86-64-v2", ["sse4_2", "popcnt", "ssse3"]),
("x86-64-v3", ["avx2", "bmi1", "bmi2", "fma", "movbe"]),
("x86-64-v4", ["avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"]),
]
def architecture():
machine = platform.machine()
flags = set()
for line in proc_cpuinfo().splitlines():
if line.startswith("flags"):
flags = set(line.split(":", 1)[1].split())
break
level = None
for name, needed in X86_LEVELS:
if all(flag in flags for flag in needed):
level = name
else:
break
return f"{machine} ({level})" if level else machine
def megahertz(path):
value = read(path)
try:
return round(int(value) / 1000)
except (TypeError, ValueError):
return None
def cpu_frequency():
cpufreq = "/sys/devices/system/cpu/cpu0/cpufreq"
base = megahertz(f"{cpufreq}/base_frequency")
top = megahertz(f"{cpufreq}/cpuinfo_max_freq")
bottom = megahertz(f"{cpufreq}/cpuinfo_min_freq")
parts = [f"base {base} MHz" if base else "base unknown"]
if bottom:
parts.append(f"min {bottom} MHz")
parts.append(f"max {top} MHz" if top else "max unknown")
return ", ".join(parts)
def cores():
physical = set()
package = core = None
for line in proc_cpuinfo().splitlines():
if line.startswith("physical id"):
package = line.split(":", 1)[1].strip()
elif line.startswith("core id"):
core = line.split(":", 1)[1].strip()
physical.add((package, core))
logical = os.cpu_count() or 0
count = len(physical) or logical
return f"{count} physical, {logical} logical"
def memory():
for line in (read("/proc/meminfo", "") or "").splitlines():
if line.startswith("MemTotal"):
kib = int(line.split()[1])
return f"{kib / 1024 / 1024:.1f} GiB"
return "unknown"
def memory_speed(previous=None):
try:
dmi = subprocess.run(
["dmidecode", "--type", "memory"], capture_output=True, text=True, timeout=10
)
except (OSError, subprocess.SubprocessError):
return previous or "unknown"
if dmi.returncode != 0:
return previous or "unknown"
speeds = set()
kinds = set()
for line in dmi.stdout.splitlines():
line = line.strip()
if line.startswith("Configured Memory Speed:") or line.startswith("Speed:"):
value = line.split(":", 1)[1].strip()
if value and "Unknown" not in value:
speeds.add(value)
elif line.startswith("Type:") and "Unknown" not in line:
kinds.add(line.split(":", 1)[1].strip())
if not speeds:
return previous or "unknown"
return ", ".join(sorted(kinds | speeds))
RELEASE_DEFAULTS = {"opt-level": 3, "lto": False, "codegen-units": 16, "debug": False}
def profile():
manifest = tomllib.loads((REPO / "Cargo.toml").read_text())
profiles = manifest.get("profile", {})
resolved = dict(RELEASE_DEFAULTS)
resolved.update(profiles.get("release", {}))
resolved.update(profiles.get("bench", {}))
resolved.pop("inherits", None)
settings = ", ".join(
f"{key} = {str(value).lower() if isinstance(value, bool) else value}"
for key, value in resolved.items()
)
return f"bench, inheriting release ({settings})"
def features():
justfile = (REPO / "justfile").read_text()
match = re.search(r'^bench_features := "([^"]+)"', justfile, re.MULTILINE)
if not match:
sys.exit("cannot read bench_features from the justfile")
return f"--no-default-features --features {match.group(1)}"
def published_memory_speed(output):
try:
previous = json.loads(output.read_text()).get("environment", {})
except (OSError, ValueError):
return None
if previous.get("cpu") != cpu_model() or previous.get("memory") != memory():
return None
speed = previous.get("memory_speed")
return None if speed in (None, "unknown") else speed
def environment(output):
valgrind = command("valgrind", "--version")
return {
"cpu": cpu_model(),
"architecture": architecture(),
"cpu_frequency": cpu_frequency(),
"cores": cores(),
"memory": memory(),
"memory_speed": memory_speed(published_memory_speed(output)),
"os": f"{platform.system()} {platform.release()}",
"rustc": command("rustc", "--version").split()[1],
"valgrind": valgrind.removeprefix("valgrind-"),
"profile": profile(),
"features": features(),
"rustflags": "",
}
def crate_version():
manifest = (REPO / "Cargo.toml").read_text()
match = re.search(r'^version = "([^"]+)"', manifest, re.MULTILINE)
if not match:
sys.exit("cannot read the crate version from Cargo.toml")
return match.group(1)
def totals(found, key, count, floor=None):
if floor is None:
floor = FLOOR
full = f"{key}/{count}"
if full not in found:
sys.exit(
f"benchmark {full} is not in the run: it failed before it wrote a summary, or it was "
"renamed (then rename it here or in benches/)"
)
measured = found[full]
if measured["instructions"] is None or measured["instructions"] < floor:
sys.exit(
f"benchmark {full} reports {measured['instructions']} instructions, which is below "
f"the floor of {floor}: collection did not cover the measured region"
)
return measured
def figures(found, key):
base = totals(found, key, COUNTS[0])
twice = totals(found, key, COUNTS[1])
if twice["instructions"] <= base["instructions"]:
sys.exit(
f"benchmark {key} does not grow with the message count "
f"({base['instructions']} -> {twice['instructions']}): the two runs measured the "
f"same work, so the slope means nothing"
)
first = totals(found, key, COLD, COLD_FLOOR)
steady = {}
for metric_name in ("instructions", "allocations"):
steady[metric_name] = rounded((twice[metric_name] - base[metric_name]) / MESSAGES)
cold = {name: first[name] for name in ("instructions", "allocations")}
return steady, cold
def build(found):
rows = []
for scenario in SCENARIOS:
framework, cold = figures(found, scenario.framework)
row = {
"name": scenario.name,
"messages": MESSAGES,
"framework": framework,
"cold": cold,
"gated": scenario.gated,
}
if scenario.note:
row["note"] = scenario.note
rows.append(row)
return rows
def report(rows):
header = (
f"{'scenario':<52}{'instructions':>14}{'allocations':>13}"
f"{'cold instr':>12}{'cold alloc':>12}"
)
print(header)
print("-" * len(header))
for row in rows:
cold = row["cold"]
print(
f"{row['name']:<52}"
f"{row['framework']['instructions']:>14}"
f"{row['framework']['allocations']:>13}"
f"{cold['instructions']:>12}"
f"{cold['allocations']:>12}"
)
print()
print(
f"instructions and allocations per message in the steady state over {MESSAGES} "
"deliveries; cold is what starting"
)
print("the service and handling the first delivery cost once")
def report_breaches(lines):
if not lines:
return
print()
print("limits breached (totals of one run, old -> new):")
for line in lines:
print(f" {line}")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("summary", type=Path, help="the JSON the benchmark run wrote")
parser.add_argument("output", type=Path, help="where to write the results document")
parser.add_argument(
"--messages",
type=int,
default=DEFAULT_MESSAGES,
help="deliveries per measured run, the count the benches were built with",
)
args = parser.parse_args()
configure(args.messages)
runs = summaries(args.summary)
breached = breaches(runs)
try:
code = build(measurements(runs))
except SystemExit:
report_breaches(breached)
sys.stdout.flush()
raise
version = crate_version()
document = {
"schema": 2,
"crate": "ruststream",
"crate_version": version,
"core_version": version,
"measured_at": datetime.now(timezone.utc).date().isoformat(),
"environment": environment(args.output),
"code": code,
}
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(document, indent=2) + "\n")
report(document["code"])
report_breaches(breached)
if __name__ == "__main__":
main()