core: AOT context image — init-cached recovers the bootstrap cost across processes
init in compile mode is ~2.4 s (tier loading, analyzer self-compile, macro recompilation), paid by every process that builds a ctx from source — each jpm-test file, embedders, workers. init-cached marshals the built ctx to a disk image (same root-env dicts as snapshot/fork) and later processes unmarshal it in ~5 ms, any process: nothing from the baking process is needed at load. The cache key fingerprints the embedded .clj stdlib (which covers jolt-core: analyzer, IR, core tiers), the .janet seed sources next to the module, the janet version, the init opts, and the env knobs that shape a ctx (JOLT_PATH/ MUTABLE/AOT_CORE/FEATURES) — any change rebuilds. Corrupt or non-ctx images fall back to a rebuild (unmarshal of garbage can 'succeed' with a scalar, so the shape is checked, not just the throw). Writes are atomic (tmp + rename) so racing cold starts never publish a torn image. JOLT_NO_IMAGE_CACHE=1 opts out; JOLT_IMAGE_CACHE_DIR overrides the location (default TMPDIR). Test consumers switch to init-cached (harness, suite-worker, conformance, the behavioral unit/integration tests); tests that validate the bootstrap itself (bootstrap-fixpoint, staged-bootstrap, aot round-trip, direct-linking) and the deps tests (tmp-dir :paths would fragment the key) keep real init. Full jpm test: 2:46 -> 1:58 (~29%). New ctx-image-test covers cold/warm, cross-process load (subprocess runs defn/redef/macros/protocols/multimethods off the baked image), per-opts keying, and corrupt-image fallback.
This commit is contained in:
parent
bf885078f9
commit
0e3584884f
24 changed files with 222 additions and 64 deletions
|
|
@ -12,7 +12,7 @@
|
|||
(use ../../src/jolt/reader)
|
||||
(import ../../src/jolt/backend :as backend)
|
||||
|
||||
(def ctx (init))
|
||||
(def ctx (init-cached))
|
||||
|
||||
# 1. A deliberate punt (letfn needs letrec IR) falls back and evaluates correctly.
|
||||
(assert (= 3 (backend/compile-and-eval ctx (parse-string "(letfn [(f [n] (+ n 1))] (f 2))")))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
(print "Eval Tests")
|
||||
|
||||
(print "1: eval literal...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= 42 (ct-eval ctx "(eval 42)")) "eval literal")
|
||||
(assert (= 3 (ct-eval ctx "(eval '(+ 1 2))")) "eval quoted form")
|
||||
(assert (= 3 (ct-eval ctx "(eval (eval '(+ 1 2)))")) "eval nested")
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# Helper: parse and eval
|
||||
# init, not bare make-ctx: with the implicit Janet root-env fallback removed
|
||||
# (Stage 3), a context without clojure.core interned can't resolve inc/+/etc.
|
||||
(def- shared-ctx (init))
|
||||
(def- shared-ctx (init-cached))
|
||||
(defn eval-str [s]
|
||||
(eval-form shared-ctx @{} (parse-string s)))
|
||||
|
||||
|
|
@ -103,13 +103,13 @@
|
|||
(print "13: locking...")
|
||||
# locking/instance? are overlay macros now (Stage 2 tier 6c) — they need the
|
||||
# full env (init loads the overlay), not a bare make-ctx.
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= 42 (eval-string ctx "(locking :lock 42)")) "locking returns body result"))
|
||||
(print " passed")
|
||||
|
||||
(print "14: instance?...")
|
||||
# instance? checks type
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (eval-string ctx "(instance? Number 42)")) "instance? Number matches number")
|
||||
(assert (= false (eval-string ctx "(instance? Number \"hello\")")) "instance? Number doesn't match string"))
|
||||
(print " passed")
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
(print "15: defmulti/defmethod...")
|
||||
# defmulti/defmethod are overlay macros now (Stage 2 jolt-eaa), so this needs the
|
||||
# full env (init loads the overlay + installs the *-setup fns), not a bare make-ctx.
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(eval-form ctx @{} (parse-string "(defmulti my-dispatch (fn* [x] (x :type)))"))
|
||||
(eval-form ctx @{} (parse-string "(defmethod my-dispatch :foo [_] :got-foo)"))
|
||||
(eval-form ctx @{} (parse-string "(defmethod my-dispatch :bar [_] :got-bar)"))
|
||||
|
|
@ -126,8 +126,8 @@
|
|||
(print " passed")
|
||||
|
||||
(print "16: deftype...")
|
||||
# deftype is an overlay macro now (Stage 2 jolt-eaa) — needs the full env (init).
|
||||
(let [ctx (init)
|
||||
# deftype is an overlay macro now (Stage 2 jolt-eaa) — needs the full env (init-cached).
|
||||
(let [ctx (init-cached)
|
||||
_ (eval-form ctx @{} (parse-string "(deftype Point [x y])"))
|
||||
_ (eval-form ctx @{} (parse-string "(def p (Point. 10 20))"))
|
||||
p-val (eval-form ctx @{} (parse-string "p"))
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
(print "17: defmacro...")
|
||||
# define a macro using defmacro special form
|
||||
# init loads clojure.core so `list` is available
|
||||
(let [ctx (init)
|
||||
(let [ctx (init-cached)
|
||||
_ (eval-form ctx @{} (parse-string "(defmacro my-when [test body] (list 'if test body nil))"))
|
||||
result (eval-form ctx @{} (parse-string "(my-when true 2)"))]
|
||||
(assert (= 2 result) "defmacro defines callable macro"))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
(print "Janet Interop Tests")
|
||||
|
||||
(print "1: field access on tables...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(def t {:a 1 :b 2})")
|
||||
(assert (= 1 (ct-eval ctx "(. t :a)")) ". field access table")
|
||||
(assert (= 2 (ct-eval ctx "(. t :b)")) ". field access table 2")
|
||||
|
|
@ -11,33 +11,33 @@
|
|||
(print " ok")
|
||||
|
||||
(print "2: field access on structs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(def s {:x 10 :y 20})")
|
||||
(assert (= 10 (ct-eval ctx "(. s :x)")) ". field access struct")
|
||||
(assert (= 20 (ct-eval ctx "(. s :y)")) ". field access struct 2"))
|
||||
(print " ok")
|
||||
|
||||
(print "3: method calls on tables...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(def obj {:greet (fn [self name] (str \"Hello \" name))})")
|
||||
(assert (= "Hello Alice" (ct-eval ctx "(. obj greet \"Alice\")")) ". method call on table"))
|
||||
(print " ok")
|
||||
|
||||
(print "4: field access via .- reader sugar...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(def t {:x 42 :len 5})")
|
||||
(assert (= 42 (ct-eval ctx "(.-x t)")) ".-x reader sugar")
|
||||
(assert (= 5 (ct-eval ctx "(.-len t)")) ".-len reader sugar"))
|
||||
(print " ok")
|
||||
|
||||
(print "5: . field access still works on deftypes...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(deftype Point [x y])")
|
||||
(assert (= 3 (ct-eval ctx "(. (Point. 3 4) x)")) ". field access deftype"))
|
||||
(print " ok")
|
||||
|
||||
(print "6: method call with multiple args...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(ct-eval ctx "(def calc {:add (fn [_ a b] (+ a b)) :mul (fn [_ a b] (* a b))})")
|
||||
(assert (= 7 (ct-eval ctx "(. calc add 3 4)")) ". method add")
|
||||
(assert (= 12 (ct-eval ctx "(. calc mul 3 4)")) ". method mul"))
|
||||
|
|
|
|||
|
|
@ -3,47 +3,47 @@
|
|||
(print "LazySeq Tests")
|
||||
|
||||
(print "1: lazy-seq from list...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (take 10 (lazy-seq [1 2 3])))")) "lazy-seq list")
|
||||
(assert (= true (ct-eval ctx "(= 3 (count (lazy-seq [1 2 3])))")) "count lazy-seq"))
|
||||
(print " ok")
|
||||
|
||||
(print "2: lazy-cat concatenation...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3 4] (take 10 (lazy-cat [1 2] [3 4])))")) "lazy-cat concat")
|
||||
(assert (= true (ct-eval ctx "(= 4 (count (lazy-cat [1 2] [3 4])))")) "lazy-cat count"))
|
||||
(print " ok")
|
||||
|
||||
(print "3: first/rest on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= 1 (first (lazy-seq [1 2 3])))")) "first lazy")
|
||||
(assert (= true (ct-eval ctx "(= 2 (first (rest (lazy-seq [1 2 3]))))")) "first rest lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "4: drop/nth on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= [3 4 5] (take 10 (drop 2 (lazy-seq [1 2 3 4 5]))))")) "drop 2 take 10")
|
||||
(assert (= true (ct-eval ctx "(= 3 (nth (lazy-seq [1 2 3 4 5]) 2))")) "nth lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "5: concat on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= 5 (count (concat (lazy-seq [1 2]) (lazy-seq [3 4 5]))))")) "concat lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "6: reverse/sort on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= [3 2 1] (reverse (lazy-seq [1 2 3])))")) "reverse lazy")
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (sort (lazy-seq [3 1 2])))")) "sort lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "7: distinct on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (distinct (lazy-seq [1 2 1 3 2])))")) "distinct lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "8: fib-seq (deferred — eager core-map needs lazy upgrade)...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(print " The cell-by-cell lazy-seq architecture is correct. concat, take,")
|
||||
(print " drop, nth, first, rest all work with self-referencing patterns.")
|
||||
(print " core-map still eagerly realizes all lazy-seq arguments, which")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# 1. Basic hash-map construction and access
|
||||
# ============================================================
|
||||
(print "1: hash-map construction...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(def m1 (ct-eval ctx "(hash-map :a 1)"))
|
||||
(assert (not (nil? m1)) "hash-map returns non-nil")
|
||||
(assert (= true (ct-eval ctx "(map? (hash-map :a 1))")) "map? returns true for PHM")
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
# 2. assoc and dissoc
|
||||
# ============================================================
|
||||
(print "2: assoc/dissoc...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(= (assoc (hash-map :a 1) :b 2) (hash-map :a 1 :b 2))")) "assoc add")
|
||||
(assert (= true (ct-eval ctx "(= (assoc (hash-map :a 1) :a 99) (hash-map :a 99))")) "assoc replace")
|
||||
(assert (= true (ct-eval ctx "(= (dissoc (hash-map :a 1 :b 2) :a) (hash-map :b 2))")) "dissoc")
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
# 3. keys, vals, merge
|
||||
# ============================================================
|
||||
(print "3: keys/vals/merge...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= 2 (ct-eval ctx "(count (keys (hash-map :a 1 :b 2)))")) "keys count")
|
||||
(assert (= 2 (ct-eval ctx "(count (vals (hash-map :a 1 :b 2)))")) "vals count")
|
||||
(assert (= true (ct-eval ctx "(= (merge (hash-map :a 1) (hash-map :b 2)) (hash-map :a 1 :b 2))")) "merge"))
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
# 4. Empty and seq
|
||||
# ============================================================
|
||||
(print "4: empty? and seq...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(assert (= true (ct-eval ctx "(empty? (hash-map))")) "empty? true")
|
||||
(assert (= false (ct-eval ctx "(empty? (hash-map :a 1))")) "empty? false")
|
||||
(assert (= 1 (ct-eval ctx "(count (seq (hash-map :a 1)))")) "seq count"))
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
# 5. Larger maps
|
||||
# ============================================================
|
||||
(print "5: larger maps...")
|
||||
(let [ctx (init)]
|
||||
(let [ctx (init-cached)]
|
||||
(eval-string ctx "
|
||||
(def big-map
|
||||
(reduce (fn [m i] (assoc m (keyword (str \"k\" i)) i))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue