jolt/bench/mono_dispatch.clj
Yogthos 5a5ea8058a Make the benchmark harness build optimized binaries on Chez
bench/run.sh was Janet-era: it invoked a 'jolt' binary and set
JOLT_DIRECT_LINK/JOLT_WHOLE_PROGRAM, none of which exist on Chez, where
'joltc run -m' runs fully unoptimized (direct-link and inline default off). So
the suite was measuring jolt's unoptimized path.

run.sh now compiles each benchmark to an optimized AOT binary (joltc build
--direct-link --opt) and times it against JVM Clojure on the same portable
source, auto-detecting the Chez kernel dev files like build-smoke.sh. Adds
bench/deps.edn so joltc resolves the namespaces, NO_JVM to skip the reference.

mandelbrot.clj dropped its jolt.png require so the JVM reference can run it; the
picture demo moved to mandelbrot_png.clj (jolt-only). README scorecard refreshed
with current Chez numbers and the two-regime read (compute ~8-10x substrate floor;
dispatch/alloc ~120-330x architectural gaps the passes don't touch). Stale
'jolt -m' header lines point at bench/run.sh.
2026-06-25 23:47:39 -04:00

45 lines
1.7 KiB
Clojure

;; mono-dispatch — protocol dispatch where every call site sees ONE record type
;; (monomorphic). This is the regime where devirtualization and a
;; call-site inline cache CAN fire — the megamorphic `dispatch` bench deliberately
;; defeats them, so this is its complement: it measures how close a proven/cached
;; monomorphic dispatch gets to a direct call. Same per-call work as `dispatch`.
;;
;; Portable Clojure (jolt + JVM Clojure).
;; bench/run.sh mono-dispatch 20000
(ns mono-dispatch)
(defprotocol Shape
(area [s])
(sides [s]))
(defrecord Circle [r] Shape (area [_] (* (* 3.14159 r) r)) (sides [_] 0))
;; homogeneous: every element is a Circle -> the call site is monomorphic
(defn build-shapes [n]
(mapv (fn [i] (->Circle (+ 1 (mod i 7)))) (range n)))
(defn sum-area [shapes]
(reduce (fn [acc s] (+ (+ acc (area s)) (sides s))) 0.0 shapes))
(defn run [iters]
(let [shapes (build-shapes 1000)]
(loop [i 0 acc 0.0]
(if (< i iters)
(recur (inc i) (+ acc (sum-area shapes)))
acc))))
(defn -main [& args]
(let [iters (if (seq args) (Integer/parseInt (first args)) 20000)]
(dotimes [_ 2] (run (quot iters 4))) ; warmup
(let [runs 3
times (mapv (fn [_]
(let [t0 (System/nanoTime)
r (run iters)
ms (/ (- (System/nanoTime) t0) 1000000.0)]
[ms r]))
(range runs))
mss (mapv first times)
mean (/ (reduce + mss) runs)]
(println "mono-dispatch iters" iters "result" (second (first times)))
(println "runs:" (mapv (fn [t] (/ (Math/round (* t 10.0)) 10.0)) mss))
(println "mean:" (/ (Math/round (* mean 10.0)) 10.0) "ms"))))