Skip to content

perf(memo): remove hot-path closures via new Fiber.Var combinators#15220

Merged
rgrinberg merged 1 commit into
mainfrom
memo-fiber-var-apply
Jun 16, 2026
Merged

perf(memo): remove hot-path closures via new Fiber.Var combinators#15220
rgrinberg merged 1 commit into
mainfrom
memo-fiber-var-apply

Conversation

@rgrinberg

@rgrinberg rgrinberg commented Jun 16, 2026

Copy link
Copy Markdown
Member

Remove per-call closure allocations from the parts of Memo that run on every
dependency edge and every node.

Fiber

Adds argument-threading combinators to Fiber.Var: get_apply,
get_apply_map, set_apply, and update_apply. They behave like
get/set/update but take a separate argument that is threaded into the
continuation, so callers on hot paths can pass a hoisted, closure-free function
instead of allocating a fresh unit -> _ fiber thunk on every call. They mirror
the existing of_thunk_apply and come with tests in src/fiber/test/var_tests.ml.

Memo

Uses the new combinators to drop per-call closures:

  • Dependency collection (deps_collector.ml): run becomes run_apply
    (threads the computation's input via set_apply); add_dep_from_caller reads
    the active collector via get_apply_map with a hoisted function; and the
    parallel combinators (parallel.ml) thread each element instead of wrapping it
    in a fun () -> ... thunk.
  • Call stack (node.ml): Call_stack.push_frame updates the call-stack
    variable and runs the body in a single update_apply effect, dropping both the
    separate read and the per-push thunk.

Behaviour is unchanged — this only reduces allocations on the hot path.

Add argument-threading combinators to Fiber.Var ([get_apply],
[get_apply_map], [set_apply], [update_apply]). They behave like
[get]/[set]/[update] but take a separate argument that is threaded into the
continuation, so callers on hot paths can pass a hoisted, closure-free
function instead of allocating a fresh [unit -> _ fiber] thunk on every call.
They mirror the existing [of_thunk_apply].

Use them to remove per-call closures from the parts of Memo that run on every
dependency edge and every node:

- Dependency collection: [Deps_collector.run] becomes [run_apply] (threads the
  computation's input through [set_apply]); [add_dep_from_caller] reads the
  active collector via [get_apply_map] with a hoisted function; and the
  parallel combinators thread each element instead of wrapping it in a
  [fun () -> ...] thunk.

- Call stack: [Call_stack.push_frame] updates the call-stack variable and runs
  the body in a single [update_apply] effect, dropping both the separate read
  and the per-push thunk.

Behaviour is unchanged; this only reduces allocations on the hot path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
@rgrinberg rgrinberg force-pushed the memo-fiber-var-apply branch from ba37e73 to 396f48b Compare June 16, 2026 09:42
@rgrinberg rgrinberg changed the title feat(fiber): add Var arg-threading combinators perf(memo): remove hot-path closures via new Fiber.Var combinators Jun 16, 2026
@rgrinberg rgrinberg merged commit 7914098 into main Jun 16, 2026
57 of 58 checks passed
@rgrinberg rgrinberg deleted the memo-fiber-var-apply branch June 16, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant