run-tests.janet runs the same file set as `jpm test` across a pool of worker processes (one `janet FILE` each, ev-based). The full gate goes from ~790s serial to ~98s here (8x), and more on CI where the heavy files don't thrash on swap. CI and the docs point at it; `jpm test` still works serially. Three things dominated the wall: - Nine integration tests cold-built a compile ctx (~8s each); switch them to api/init-cached so they share the prebuilt image. The cache key already fingerprints the ctx-shaping env vars, so the direct-link ones share one DL image and the rest share the plain one. - core-bench's main ran on every gate (~35s of benchmark loops that assert nothing); gate it behind JOLT_BENCH=1. - cli-test spawned `janet src/jolt/main.janet` ~20 times at ~8s cold each (340s under parallel load, and it was the whole wall); prefer build/jolt (~20ms baked ctx) when present, fall back to from-source for an unbuilt tree. type-check-test stays on cold init: a snapshot-loaded ctx loses the success checker's op/msg detail (jolt-vley). jolt-pria tracks caching from-source startup generally, which would let cli-test drop the build/jolt preference. Co-authored-by: Yogthos <yogthos@gmail.com>
50 lines
2.5 KiB
Text
50 lines
2.5 KiB
Text
# Performance baseline for the clojure.core migration (jolt-1j0).
|
|
#
|
|
# Times representative core operations end-to-end (compile path) so a phase that
|
|
# moves fns from native Janet to the self-hosted Clojure overlay can be checked
|
|
# for regressions. Same programs before/after a phase -> relative delta is the
|
|
# migration's perf impact. Run: JOLT_BENCH=1 janet test/bench/core-bench.janet
|
|
# (skipped under `jpm test` — it asserts nothing; see main).
|
|
#
|
|
# Each program carries its own internal iteration so the measured work dominates
|
|
# parse/compile overhead. Reports the min of N runs (least noisy).
|
|
|
|
(import ../../src/jolt/api :as api)
|
|
|
|
(def runs 5)
|
|
|
|
(def benches
|
|
[[:fib "(defn fib [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))) (fib 28)"]
|
|
[:seq-pipe "(loop [i 0 a 0] (if (< i 300) (recur (inc i) (+ a (reduce + 0 (map inc (filter even? (range 200)))))) a))"]
|
|
[:reduce "(loop [i 0 a 0] (if (< i 2000) (recur (inc i) (+ a (reduce + 0 (range 500)))) a))"]
|
|
[:into-vec "(loop [i 0 a 0] (if (< i 1000) (recur (inc i) (+ a (count (into [] (map inc (range 100)))))) a))"]
|
|
[:map-build "(loop [i 0 a 0] (if (< i 2000) (recur (inc i) (+ a (count (reduce (fn [m k] (assoc m k k)) {} (range 50))))) a))"]
|
|
[:map-read "(let [m (zipmap (range 100) (range 100))] (loop [i 0 a 0] (if (< i 5000) (recur (inc i) (+ a (get m (mod i 100) 0))) a)))"]
|
|
[:str-join "(loop [i 0 a 0] (if (< i 1000) (recur (inc i) (+ a (count (apply str (map str (range 100)))))) a))"]
|
|
[:hof "(loop [i 0 a 0] (if (< i 2000) (recur (inc i) (+ a (reduce + 0 (map (comp inc inc) (range 200))))) a))"]])
|
|
|
|
(defn time-bench [ctx src]
|
|
(var best math/inf)
|
|
(for _ 0 runs
|
|
(def t0 (os/clock))
|
|
(api/load-string ctx src)
|
|
(def dt (* 1000 (- (os/clock) t0)))
|
|
(when (< dt best) (set best dt)))
|
|
best)
|
|
|
|
(defn main [&]
|
|
# `jpm test` recurses test/ and would run this every gate, but it's a manual
|
|
# perf tool that asserts nothing (just reports timings) — so skip it unless
|
|
# opted in with JOLT_BENCH=1. Keeps ~35s of unasserted benchmark work out of
|
|
# the correctness gate (same pattern as suite-worker's no-arg no-op).
|
|
(unless (os/getenv "JOLT_BENCH")
|
|
(print "core-bench: SKIP (set JOLT_BENCH=1 to run)")
|
|
(os/exit 0))
|
|
(def ctx (api/init {:compile? true}))
|
|
(print "bench (compile mode), min of " runs " runs, ms:")
|
|
(var total 0)
|
|
(each [name src] benches
|
|
(def ms (time-bench ctx src))
|
|
(+= total ms)
|
|
(printf " %-10s %8.2f ms" name ms))
|
|
(printf " %-10s %8.2f ms" "TOTAL" total))
|