Skip to content

Commit 9f18073

Browse files
committed
Add lz77 benchmark
1 parent 86db07d commit 9f18073

2 files changed

Lines changed: 461 additions & 0 deletions

File tree

uvclang/bench_lz77.sh

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/bin/sh
2+
#
3+
# bench_lz77.sh — benchmark examples/lz77.c three ways and confirm they all
4+
# compute the same result:
5+
#
6+
# 1. UVM (release) : uvclang -O2 lowers the C to UVM asm, run in
7+
# the release UVM interpreter.
8+
# 2. native -O2, SIMD on : host clang at -O2 with the auto-vectorizers
9+
# enabled (a normal optimized native build).
10+
# 3. native -O2, SIMD off : host clang at -O2 with -fno-vectorize
11+
# -fno-slp-vectorize, i.e. scalar codegen.
12+
#
13+
# (2) vs (3) shows how much auto-vectorization buys on native; (3) is the
14+
# closest apples-to-apples baseline for the scalar UVM interpreter, so times are
15+
# normalized against it. All three print the same checksum line, which the
16+
# script cross-checks so a silent divergence can't masquerade as a speedup.
17+
#
18+
# Env overrides:
19+
# CC native C compiler (default: Homebrew LLVM clang, else cc)
20+
# UVM_REPS timed repetitions for the UVM run (default 3)
21+
# NATIVE_REPS timed repetitions for each native run (default 5)
22+
#
23+
# Timing is best-of-N wall clock via python3's perf_counter (sub-millisecond).
24+
25+
set -eu
26+
cd "$(dirname "$0")"
27+
ROOT=$(cd .. && pwd)
28+
SRC=examples/lz77.c
29+
30+
TMP=$(mktemp -d)
31+
trap 'rm -rf "$TMP"' EXIT
32+
33+
# Resolve the native compiler: explicit $CC, else Homebrew LLVM clang (same
34+
# compiler the uvclang front-end uses, so native and UVM share a front end),
35+
# else the platform cc.
36+
if [ -n "${CC:-}" ]; then
37+
:
38+
elif [ -x /opt/homebrew/opt/llvm/bin/clang ]; then
39+
CC=/opt/homebrew/opt/llvm/bin/clang
40+
else
41+
CC=cc
42+
fi
43+
44+
echo "native compiler : $CC"
45+
46+
echo "building uvclang (release)..."
47+
cargo build --release -q
48+
UVCLANG="$ROOT/uvclang/target/release/uvclang"
49+
50+
echo "building UVM (release)..."
51+
cargo build --release -q --manifest-path "$ROOT/vm/Cargo.toml"
52+
UVM="$ROOT/vm/target/release/uvm"
53+
54+
echo "compiling $SRC ..."
55+
# UVM build: uvclang drives clang for the x86_64 UVM target (scalar; its
56+
# canonical flags already include -fno-vectorize) and produces UVM asm.
57+
"$UVCLANG" -O2 "$SRC" -o "$TMP/lz77.asm"
58+
# Native builds: host target, SIMD on and SIMD off.
59+
"$CC" -O2 "$SRC" -o "$TMP/native_simd"
60+
"$CC" -O2 -fno-vectorize -fno-slp-vectorize "$SRC" -o "$TMP/native_nosimd"
61+
62+
export UVM ASM="$TMP/lz77.asm"
63+
export NAT_SIMD="$TMP/native_simd" NAT_NOSIMD="$TMP/native_nosimd"
64+
export UVM_REPS="${UVM_REPS:-3}" NATIVE_REPS="${NATIVE_REPS:-5}"
65+
66+
python3 - <<'PY'
67+
import os, subprocess, sys, time
68+
69+
def bench(label, reps, cmd):
70+
best = None
71+
out = ""
72+
for _ in range(reps):
73+
t0 = time.perf_counter()
74+
r = subprocess.run(cmd, capture_output=True, text=True)
75+
dt = time.perf_counter() - t0
76+
if r.returncode != 0:
77+
print(f"\n{label}: FAILED (exit {r.returncode})\n{r.stderr}", file=sys.stderr)
78+
sys.exit(1)
79+
out = r.stdout
80+
best = dt if best is None else min(best, dt)
81+
return best, out
82+
83+
configs = [
84+
("UVM (release)", int(os.environ["UVM_REPS"]), [os.environ["UVM"], os.environ["ASM"]]),
85+
("native -O2, SIMD on", int(os.environ["NATIVE_REPS"]), [os.environ["NAT_SIMD"]]),
86+
("native -O2, SIMD off", int(os.environ["NATIVE_REPS"]), [os.environ["NAT_NOSIMD"]]),
87+
]
88+
89+
results = [(label, *bench(label, reps, cmd), reps) for label, reps, cmd in configs]
90+
91+
def checksum(out):
92+
for line in out.splitlines():
93+
if "checksum=" in line:
94+
return line.split("checksum=", 1)[1].strip()
95+
return None
96+
97+
baseline_csum = checksum(results[0][2])
98+
parity = all(checksum(out) == baseline_csum for _, _, out, _ in results)
99+
100+
# Normalize against native SIMD-off (the scalar apples-to-apples baseline).
101+
base_t = next(t for label, t, _, _ in results if label == "native -O2, SIMD off")
102+
103+
print()
104+
print("== lz77 benchmark ==")
105+
for line in results[0][2].splitlines(): # the program's own summary (identical for all)
106+
print(" " + line)
107+
print()
108+
print(f" {'configuration':24s} {'best time':>11s} {'vs SIMD-off':>11s}")
109+
print(" " + "-" * 54)
110+
for label, t, _, reps in results:
111+
print(f" {label:24s} {t*1000:8.2f} ms {t/base_t:9.2f}x (best of {reps})")
112+
print()
113+
114+
if parity:
115+
print(" checksums MATCH across all three builds")
116+
else:
117+
print(" checksums DIFFER -- builds disagree on the result:")
118+
for label, _, out, _ in results:
119+
print(f" {label:24s} {checksum(out)}")
120+
sys.exit(1)
121+
PY

0 commit comments

Comments
 (0)