import json
import sys
TABLES = [("Polylines", ["all", "one"]), ("Polygons", ["polygon_all", "polygon_one"])]
BASELINES = ["stripe", "geo"]
NOTES = """\
Percentages in parentheses: how much more than the lowest count of the same operation in its row.
Baselines (`benches/baseline/mod.rs`) clip the same inputs into the same tiles in `f64`, computing \
intersection points and dropping each tile's output, where the toolkit keeps the original vertices and \
stores every tile's output. `stripe`: a geojson-vt-style stripe clipper, the realistic target. `geo`: \
`geo`'s `BooleanOps`, an upper bound; too slow to count in CI, `just bench geo` counts it.
"""
BASE_NOTES = """\
Change from the base commit: `=` same count, `~0%` within 0.05%, `new` not in the base. Only the \
toolkit's slicers are counted on the base: the baselines are the same code.
"""
def instructions(profile):
if "data" in profile:
return int(profile["data"]["total"]["metrics"]["Ir"]["values"]["new"])
(count,) = profile["summaries"]["total"]["summary"]["Callgrind"]["Ir"]["metrics"]["Left"].values()
return int(count)
def load(path):
counts, order = {}, {}
with open(path, encoding="utf-8") as f:
for line in f:
if not line.startswith("{"):
continue
bench = json.loads(line)
counts[bench["function_name"], bench["id"]] = instructions(bench["profiles"][0])
order.setdefault(bench["function_name"], []).append(bench["id"])
return counts, order
def compact(n):
for div, unit in ((1e9, "G"), (1e6, "M"), (1e3, "k")):
if n >= div:
return f"{n / div:.3g}{unit}"
return str(n)
def above_best(n, best):
if n == best:
return "best"
pct = (n / best - 1) * 100
if pct >= 1000:
return f"{n / best:.0f}×"
return f"+{pct:.2g}%" if pct < 10 else f"+{pct:.0f}%"
def change(new, old):
if old is None:
return "new"
if new == old:
return "="
pct = (new - old) / old * 100
return "~0%" if abs(pct) < 0.05 else f"{pct:+.1f}%"
def table(header, rows):
widths = [max(len(row[i]) for row in [header, *rows]) for i in range(len(header))]
def line(cells):
padded = [c.ljust(w) if i == 0 else c.rjust(w) for i, (c, w) in enumerate(zip(cells, widths))]
return "| " + " | ".join(padded) + " |"
rule = "|" + "|".join("-" * (w + 2) if i == 0 else "-" * (w + 1) + ":" for i, w in enumerate(widths))
return "\n".join([line(header), rule + "|", *map(line, rows)])
def main():
head, order = load(sys.argv[1])
base = load(sys.argv[2])[0] if len(sys.argv) > 2 else None
listed = {op for _, ops in TABLES for op in ops}
known = listed | {f"{op}_{b}" for op in listed for b in BASELINES}
other = [f for f in order if f not in known]
for title, ops in TABLES + ([("Other", other)] if other else []):
columns = [[op] + [f"{op}_{b}" for b in BASELINES if f"{op}_{b}" in order] for op in ops if op in order]
if not columns:
continue
ids = []
for f in (f for funcs in columns for f in funcs):
ids += [i for i in order[f] if i not in ids]
rows = []
for i in ids:
row = [i]
for funcs in columns:
best = min((head[f, i] for f in funcs if (f, i) in head), default=None)
row += [f"{compact(head[f, i])} ({above_best(head[f, i], best)})" if (f, i) in head else "n/a"
for f in funcs]
rows.append(row)
header = ["Input"] + [f.replace("_", " ") for funcs in columns for f in funcs]
print(f"**{title}**: CPU instructions (lower is better)\n\n{table(header, rows)}\n")
if base is not None:
slicers = [funcs[0] for funcs in columns]
rows = [[i] + [change(head[f, i], base.get((f, i))) if (f, i) in head else "n/a" for f in slicers]
for i in ids]
rows = [row for row in rows if any(c != "n/a" for c in row[1:])]
header = ["Input"] + [f.replace("_", " ") for f in slicers]
print(f"**{title}**: CPU instructions, change from the base commit\n\n{table(header, rows)}\n")
print(NOTES + (BASE_NOTES if base is not None else ""), end="")
if __name__ == "__main__":
main()