Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 50s
execute.sx folds a plan, runs each node via an injected runner (perform in prod, op-table in tests), and memoizes results in a lib/persist kv backend keyed by content-id. Incremental recompute falls out of content addressing: a leaf change reassigns ids across its dirty closure, so re-running hits the unchanged nodes and recomputes only the closure (cold 5 -> rerun 0 -> change 3). Cross-dag subgraph sharing verified. execute 15/15, total 69/69. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
189 lines
4.5 KiB
Plaintext
189 lines
4.5 KiB
Plaintext
; Phase 4 — Execute: effect interpreter + content-addressed memo + incremental.
|
|
|
|
(define ex-RT (artdag/op-table-runner {:in (fn (params inputs) (get params :v)) :add (fn (params inputs) (+ (nth inputs 0) (nth inputs 1))) :inc (fn (params inputs) (+ 1 (first inputs)))}))
|
|
|
|
; two-leaf diamond: p,q leaves; b=inc(p); c=inc(q); d=add(b,c)
|
|
(define
|
|
ex-D1
|
|
(artdag/build
|
|
(list
|
|
(list "p" "in" (list) {:v 10})
|
|
(list "q" "in" (list) {:v 20})
|
|
(list "b" "inc" (list "p") {})
|
|
(list "c" "inc" (list "q") {})
|
|
(list "d" "add" (list "b" "c") {} true))))
|
|
|
|
; same shape, leaf q changed (20 -> 21)
|
|
(define
|
|
ex-D2
|
|
(artdag/build
|
|
(list
|
|
(list "p" "in" (list) {:v 10})
|
|
(list "q" "in" (list) {:v 21})
|
|
(list "b" "inc" (list "p") {})
|
|
(list "c" "inc" (list "q") {})
|
|
(list "d" "add" (list "b" "c") {} true))))
|
|
|
|
; a different dag that shares the p->b subgraph with ex-D1, plus z=inc(b)
|
|
(define
|
|
ex-D3
|
|
(artdag/build
|
|
(list
|
|
(list "p" "in" (list) {:v 10})
|
|
(list "b" "inc" (list "p") {})
|
|
(list "z" "inc" (list "b") {}))))
|
|
|
|
; ---- full execution ----
|
|
|
|
(artdag-test
|
|
"full run: result is correct"
|
|
(let
|
|
((cache (persist/open)))
|
|
(artdag/result-of
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/dag-id ex-D1 "d")))
|
|
32)
|
|
|
|
(artdag-test
|
|
"full run: cold cache recomputes every node"
|
|
(let
|
|
((cache (persist/open)))
|
|
(artdag/recompute-count (artdag/run ex-D1 ex-RT cache)))
|
|
5)
|
|
|
|
(artdag-test
|
|
"full run: cold cache has no hits"
|
|
(let
|
|
((cache (persist/open)))
|
|
(artdag/hit-count (artdag/run ex-D1 ex-RT cache)))
|
|
0)
|
|
|
|
; ---- memoization ----
|
|
|
|
(artdag-test
|
|
"re-run unchanged: zero recomputes"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/recompute-count (artdag/run ex-D1 ex-RT cache))))
|
|
0)
|
|
|
|
(artdag-test
|
|
"re-run unchanged: all cache hits"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/hit-count (artdag/run ex-D1 ex-RT cache))))
|
|
5)
|
|
|
|
(artdag-test
|
|
"re-run unchanged: result preserved"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/result-of
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/dag-id ex-D1 "d"))))
|
|
32)
|
|
|
|
; ---- incremental recompute (the keystone) ----
|
|
|
|
(artdag-test
|
|
"leaf change recomputes only the dirty closure (count)"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/recompute-count (artdag/run ex-D2 ex-RT cache))))
|
|
3)
|
|
|
|
(artdag-test
|
|
"leaf change: unchanged nodes are cache hits"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/hit-count (artdag/run ex-D2 ex-RT cache))))
|
|
2)
|
|
|
|
(artdag-test
|
|
"leaf change: recomputed set is exactly q,c,d"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/recomputed (artdag/run ex-D2 ex-RT cache))))
|
|
(artdag/sort-strings
|
|
(list
|
|
(artdag/dag-id ex-D2 "q")
|
|
(artdag/dag-id ex-D2 "c")
|
|
(artdag/dag-id ex-D2 "d"))))
|
|
|
|
(artdag-test
|
|
"leaf change: untouched sibling p is reused"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/member?
|
|
(artdag/dag-id ex-D2 "p")
|
|
(get (artdag/run ex-D2 ex-RT cache) :hits))))
|
|
true)
|
|
|
|
(artdag-test
|
|
"leaf change: new result is correct"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/result-of
|
|
(artdag/run ex-D2 ex-RT cache)
|
|
(artdag/dag-id ex-D2 "d"))))
|
|
33)
|
|
|
|
; ---- explicit dirty-only execution ----
|
|
|
|
(artdag-test
|
|
"run-dirty: schedules only the changed closure"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/recompute-count
|
|
(artdag/run-dirty ex-D2 (list (artdag/dag-id ex-D2 "q")) ex-RT cache))))
|
|
3)
|
|
|
|
; ---- cross-dag cache sharing (content addressing) ----
|
|
|
|
(artdag-test
|
|
"shared subgraph hits cache across different dags"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/recompute-count (artdag/run ex-D3 ex-RT cache))))
|
|
1)
|
|
|
|
(artdag-test
|
|
"shared subgraph: p and b reused across dags"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/hit-count (artdag/run ex-D3 ex-RT cache))))
|
|
2)
|
|
|
|
(artdag-test
|
|
"shared subgraph: z still computes correctly"
|
|
(let
|
|
((cache (persist/open)))
|
|
(begin
|
|
(artdag/run ex-D1 ex-RT cache)
|
|
(artdag/result-of
|
|
(artdag/run ex-D3 ex-RT cache)
|
|
(artdag/dag-id ex-D3 "z"))))
|
|
12)
|