Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions bench/dynamic-upm-compile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Measure UPM source-change builds in a prewarmed store.

Each trial gets a fresh source snapshot with an equal-length Go comment added
to every local Go file. This invalidates UPM packages without deleting store
objects. Some external cgo packages may also rebuild; it is not a cold-store
benchmark or an isolated measurement of the compiler.
"""
import argparse
import json
import os
import pathlib
import shutil
import subprocess
import tempfile
import time

p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--drvgo", type=pathlib.Path, required=True)
p.add_argument("--source", type=pathlib.Path, required=True)
p.add_argument("--iets", required=True)
p.add_argument("--runs", type=int, default=3)
p.add_argument("--output", type=pathlib.Path, required=True)
a = p.parse_args()
if a.runs < 1:
p.error("--runs must be positive")
env = dict(os.environ, NIX_REMOTE="daemon", IETS_PROGRESS="false")
results = []
with tempfile.TemporaryDirectory(prefix="upm-compile-") as tmp:
for trial in range(a.runs):
clients = ["iets", "determinate"]
if trial % 2:
clients.reverse()
for client in clients:
src = pathlib.Path(tmp) / f"{trial}-{client}"
shutil.copytree(a.source, src)
for path in src.rglob("*.go"):
path.chmod(0o644)
with path.open("a") as f:
f.write(f"\n// benchmark {pathlib.Path(tmp).name} {trial:03d} {client:11s}\n")
expr = f'(import ./examples/upm/dynamic.nix {{ source = {src}; }}).package'
cmd = ([a.iets, "build", "--impure", "--no-tracing-cache", "--workers", "1"]
if client == "iets" else ["nix-build", "--no-out-link", "--max-jobs", "4", "--cores", "0"])
cmd += ["-E", expr]
start = time.perf_counter()
r = subprocess.run(cmd, cwd=a.drvgo, env=env, text=True, capture_output=True)
elapsed = time.perf_counter() - start
log = a.output.with_suffix(f".{trial}-{client}.log")
log.write_text(r.stderr)
if r.returncode:
raise RuntimeError(f"{cmd} failed: see {log}")
output = r.stdout.strip()
smoke = subprocess.run([output + "/bin/upm", "--help"],
text=True, capture_output=True)
if smoke.returncode:
raise RuntimeError(f"UPM smoke test failed: {smoke.stderr}")
results.append(dict(client=client, trial=trial, seconds=elapsed,
output=output, command=cmd, help_exit=smoke.returncode))
a.output.write_text(json.dumps({
"note": "Fresh UPM source snapshots in a prewarmed store; includes planning, evaluation, compilation (including invalidated cgo dependencies), database generation and wrapping.",
"samples": results,
}, indent=2) + "\n")
print(client, trial, elapsed, flush=True)
74 changes: 74 additions & 0 deletions bench/dynamic-upm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""Alternate UPM dynamic-derivation evaluation and warm builds.

Run after provisioning drvgo's pinned toolchain and building UPM once.
Compilation is deliberately not inferred from a cached realization.
"""
import argparse
import json
import os
import pathlib
import statistics
import subprocess
import time

p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--drvgo", required=True, type=pathlib.Path)
p.add_argument("--source", required=True, help="Pinned UPM source store path")
p.add_argument("--iets", required=True)
p.add_argument("--runs", type=int, default=5)
p.add_argument("--output", type=pathlib.Path, required=True)
a = p.parse_args()
if a.runs < 1:
p.error("--runs must be positive")
expr = f'(import ./examples/upm/dynamic.nix {{ source = {a.source}; }}).package'
commands = {
"eval": {
"iets": [a.iets, "eval", "--impure", "--raw", "--no-tracing-cache",
"--workers", "1", "-E", expr + ".drvPath"],
# Read-write mode is essential: outputOf reads its producer's .drv.
"determinate": ["nix-instantiate", "--eval", "--strict", "--read-write-mode",
"-E", expr + ".drvPath"],
},
"warm-build": {
"iets": [a.iets, "build", "--impure", "--no-tracing-cache",
"--workers", "1", "-E", expr],
"determinate": ["nix-build", "--no-out-link", "-E", expr],
},
}
env = dict(os.environ, NIX_REMOTE="daemon", IETS_PROGRESS="false")
records = []
for phase, clients in commands.items():
expected = None
for run in range(a.runs + 1):
for client in (["iets", "determinate"] if run % 2 == 0
else ["determinate", "iets"]):
start = time.perf_counter()
r = subprocess.run(clients[client], cwd=a.drvgo, env=env,
text=True, capture_output=True)
elapsed = time.perf_counter() - start
if r.returncode:
raise RuntimeError(f"{clients[client]}:\n{r.stderr}")
result = r.stdout.strip().strip('"')
if expected is None:
expected = result
if result != expected:
raise RuntimeError(f"{phase}: results differ: {result} != {expected}")
if run:
records.append(dict(phase=phase, client=client, run=run,
seconds=elapsed, result=result))
summary = {
phase: {
client: statistics.median(x["seconds"] for x in records
if x["phase"] == phase and x["client"] == client)
for client in clients
} for phase, clients in commands.items()
}
report = dict(commands=commands, samples=records, medians_seconds=summary,
source=a.source,
nix_version=subprocess.check_output(["nix", "--version"], text=True).strip(),
drvgo_revision=subprocess.check_output(
["git", "-C", str(a.drvgo), "rev-parse", "HEAD"], text=True).strip(),
note="Warm page/store caches; tracing cache disabled; compilation excluded.")
a.output.write_text(json.dumps(report, indent=2) + "\n")
print(json.dumps(summary, indent=2))
95 changes: 95 additions & 0 deletions bench/results/dynamic-upm-compile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"note": "Fresh source builds in a prewarmed store; includes invalidated external cgo dependencies, planning, evaluation, database generation and wrapping.",
"samples": [
{
"client": "iets",
"trial": 0,
"seconds": 89.08709269700012,
"output": "/nix/store/g1dxrz346vxd3iziwy7lbb6bimg3cmx0-upm-drvgo",
"command": [
"/workspace/iets/target/release/iets",
"build",
"--impure",
"--no-tracing-cache",
"--workers",
"1",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/0-iets; }).package"
],
"help_exit": 0
},
{
"client": "determinate",
"trial": 0,
"seconds": 88.91069620399958,
"output": "/nix/store/jrh0l9v6m9sqk4hn1pf9a4x74skh5i0h-upm-drvgo",
"command": [
"nix-build",
"--no-out-link",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/0-determinate; }).package"
],
"help_exit": 0
},
{
"client": "determinate",
"trial": 1,
"seconds": 89.80313975599984,
"output": "/nix/store/y3fbdpkkvyhlavd31kgbyq9zgzgz1phk-upm-drvgo",
"command": [
"nix-build",
"--no-out-link",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/1-determinate; }).package"
],
"help_exit": 0
},
{
"client": "iets",
"trial": 1,
"seconds": 84.0740111820005,
"output": "/nix/store/l4f61bkb8j9idgxp48fwbf0jydrn99aq-upm-drvgo",
"command": [
"/workspace/iets/target/release/iets",
"build",
"--impure",
"--no-tracing-cache",
"--workers",
"1",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/1-iets; }).package"
],
"help_exit": 0
},
{
"client": "iets",
"trial": 2,
"seconds": 81.07874024699959,
"output": "/nix/store/ydh2p8dbqa13sm8wbcv4d5cvcxbnn98z-upm-drvgo",
"command": [
"/workspace/iets/target/release/iets",
"build",
"--impure",
"--no-tracing-cache",
"--workers",
"1",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/2-iets; }).package"
],
"help_exit": 0
},
{
"client": "determinate",
"trial": 2,
"seconds": 89.69095421800012,
"output": "/nix/store/1f4k1l1w0qfs2fcb3rmw0r0kgx7zghcx-upm-drvgo",
"command": [
"nix-build",
"--no-out-link",
"-E",
"(import ./examples/upm/dynamic.nix { source = /tmp/upm-compile-yr_ropw8/2-determinate; }).package"
],
"help_exit": 0
}
]
}
Loading
Loading