Scope whole-program optimization to app namespaces (jolt-87e) (#126)

Under JOLT_OPTIMIZE a -m program run inferred + specialized EVERY loaded
namespace, including every transitive dependency. On a dep-heavy app that's
prohibitive: malli-app cold-started in ~2m10s (hundreds of dep namespaces, each
run through the per-form inline + inference passes).

The closed world a whole-program pass reasons over is the APP, not its
libraries. jolt-deps now passes the project's own source roots (its deps.edn
:paths) to the runtime as JOLT_APP_PATHS. A namespace loaded from an app root
gets full optimization (and joins the one whole-program fixpoint); a dependency
namespace compiles at default cost — :inline? off for its load, so the per-form
optimize passes don't run over library code — staying direct-linked but
generically typed (the open-world default). With no app roots declared (a bare
program run, or jolt without jolt-deps) everything counts as app, so behavior is
unchanged.

malli-app JOLT_OPTIMIZE cold start: 2m10s -> 4.5s. Compute-heavy programs whose
hot code is their own namespaces (the typed ray tracer) are unaffected — their
code is app code and still fully optimized (9s/frame render). Applied at runtime
in main for the same baked-at-build-time reason as JOLT_PATH; added to the
ctx-image cache key. Help text corrected: optimization is opt-in, not default.

Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
Dmitri Sotnikov 2026-06-15 18:02:54 +00:00 committed by GitHub
parent d200725811
commit d223f40c91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 159 additions and 12 deletions

View file

@ -0,0 +1,67 @@
# Whole-program inference scoped to app namespaces (jolt-87e).
#
# Auto-whole-program (a -m program run under direct-link) used to defer EVERY
# loaded namespace — including every transitive dependency — into one closed-
# world fixpoint, which is prohibitive on dep-heavy apps (hundreds of dep nses;
# a ~2-minute cold start on malli). With app source roots declared (JOLT_APP_PATHS
# / jolt-deps, here :app-paths), only the app's OWN namespaces join the whole-
# program batch; dependency namespaces skip inference (they stay direct-linked
# but generically typed — the open-world default). With NO app roots declared,
# every namespace is treated as app (whole-program over everything, pre-87e).
(use ../../src/jolt/api)
(var failures 0)
(defn- check [label got want]
(unless (= got want)
(++ failures)
(printf "FAIL [%s] got %q want %q" label got want)))
# Lay down an app root and a dep root under a tmp dir.
(def tmp (string (os/getenv "TMPDIR") "jolt-87e-" (os/time)))
(def app-root (string tmp "/app"))
(def dep-root (string tmp "/dep"))
(os/mkdir tmp) (os/mkdir app-root) (os/mkdir dep-root)
(spit (string dep-root "/mydep.clj")
"(ns mydep)\n(defn helper [x] (+ x 1))\n")
(spit (string app-root "/myapp.clj")
"(ns myapp (:require [mydep]))\n(defn run [x] (mydep/helper x))\n")
(defn- with-wp [f]
(def saved (os/getenv "JOLT_WHOLE_PROGRAM"))
(os/setenv "JOLT_WHOLE_PROGRAM" "1")
(defer (os/setenv "JOLT_WHOLE_PROGRAM" saved) (f)))
# --- app roots declared: only the app ns defers into the batch ---------------
(with-wp
(fn []
(let [ctx (init {:compile? true :direct-linking? true
:paths [app-root dep-root] :app-paths [app-root]})]
(check "whole-program is on" (truthy? (get (ctx :env) :whole-program?)) true)
(eval-string ctx "(require '[myapp])")
(let [deferred (or (get (ctx :env) :inferred-nses) @[])]
(check "app ns deferred to batch" (truthy? (index-of "myapp" deferred)) true)
(check "dep ns NOT in batch (per-ns inferred)"
(truthy? (index-of "mydep" deferred)) false))
# still runs correctly after the (scoped) whole-program pass
(when-let [ip (get (ctx :env) :infer-program!)] (protect (ip ctx)))
(check "scoped whole-program program still correct"
(eval-string ctx "(myapp/run 41)") 42))))
# --- no app roots declared: every ns defers (pre-87e whole-program) ----------
(with-wp
(fn []
(let [ctx (init {:compile? true :direct-linking? true
:paths [app-root dep-root]})]
(eval-string ctx "(require '[myapp])")
(let [deferred (or (get (ctx :env) :inferred-nses) @[])]
(check "no app roots: app ns deferred" (truthy? (index-of "myapp" deferred)) true)
(check "no app roots: dep ns ALSO deferred"
(truthy? (index-of "mydep" deferred)) true))
(when-let [ip (get (ctx :env) :infer-program!)] (protect (ip ctx)))
(check "unscoped whole-program still correct"
(eval-string ctx "(myapp/run 9)") 10))))
(if (pos? failures)
(do (printf "app-scope-wholeprogram: %d failure(s)" failures) (os/exit 1))
(print "app-scope-wholeprogram: all cases passed"))