Skip to content

perf(fiber): defunctionalize scheduler computations - #15723

Open
rgrinberg wants to merge 12 commits into
ocaml:mainfrom
rgrinberg:push-rwxoykppkvsk
Open

perf(fiber): defunctionalize scheduler computations#15723
rgrinberg wants to merge 12 commits into
ocaml:mainfrom
rgrinberg:push-rwxoykppkvsk

Conversation

@rgrinberg

Copy link
Copy Markdown
Member

Replace Fiber's closure-heavy internal CPS machinery with typed representations for computations, continuations, deferred scheduler work, parallel completion, and error handling. The public Fiber API is unchanged, and caller-provided callbacks remain opaque function payloads.

The commits are ordered so that each representation change remains reviewable: first continuation and unwind frames, then computation and scheduler work nodes, followed by parallel and error-handling specializations. A test immediately before the computation rewrite records the existing reusable-thunk semantics.

Across warmed null self-builds, the complete stack measured:

Target Minor allocation Promoted allocation Live heap Peak heap Instructions Wall time
@install -13.88% -5.71% -5.89% -6.14% -1.17% -3.28%
@check -14.24% -5.46% -5.49% -5.85% -1.58% -2.02%
unit tests -15.04% -5.77% -4.13% -5.81% -2.71% -4.48%

Major-GC time fell by 2.00%, 4.78%, and 10.25% respectively, while peak RSS fell by 5.51%, 6.17%, and 4.41%.

Reverse the mutable ivar reader chain in place and let the scheduler consume it through one reusable Readers job. This avoids allocating a Job for every resumed reader while preserving FIFO order.

On a no-op @install self-build this reduced sampled promoted allocation by about 0.4M words.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Represent continuations introduced by map and bind with typed frames while
keeping a function frame as the escape hatch for arbitrary callbacks. Carry
these continuations through effects and the scheduler without changing the
public Fiber API.

On a no-op @install self-build this reduced minor allocation by 14.1M
words, promoted allocation by 1.1M words, the live heap by 0.6M words,
and Cachegrind instructions by 0.8%. Native wall time remained neutral.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Represent the continuations used by Fiber variable helpers and context
unwinding directly instead of wrapping them in function closures. This keeps
the function escape hatch for arbitrary callbacks while making common
internal control transfers smaller.

On a no-op @install self-build this reduced minor allocation by 3.0M
words, promoted allocation by 1.0M words, and Cachegrind instructions by
1.5% on top of explicit map and bind continuations.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Represent runs of up to three adjacent map continuations with one typed
frame. This shortens promoted continuation chains and reduces scheduler
dispatch without changing map order or the public API.

On a no-op @install self-build this reduced promoted allocation by 0.25M
words, peak heap size by 0.15M words, and Cachegrind instructions by
0.3%. Minor allocation increased by 0.46M words and native wall time
remained neutral.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Check that constructing a thunk fiber does not execute it and that running the
same fiber twice executes the thunk twice. This guards the documented reuse
semantics before changing the internal computation representation.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Replace the internal CPS computation function with typed computation nodes and
interpret computation and continuation spines directly in the scheduler. Keep
user callbacks as opaque function payloads and pair internal primitive runners
with explicit state instead of allocated closures.

On a no-op @install self-build this reduced minor allocation by 23.8M
words, promoted allocation by 1.2M words, the live heap by 1.1M words,
and Cachegrind instructions by 1.1%. Alternating native measurements also
showed lower wall time.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Store deferred fork loops and fiber applications as typed work items instead
of allocating thunk closures and wrapping them in scheduler continuations.
Keep a function-backed work item for module-local loops that still require an
opaque escape hatch.

On a no-op @install self-build this reduced minor allocation by 1.0M words
and slightly reduced promoted allocation and Cachegrind instructions.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Replace per-child completion closures in parallel iteration, map-reduce, array
mapping, fork-and-join, and Svar wakeups with typed continuation frames. Share
one completion frame where all children perform the same update.

On a no-op @install self-build this reduced minor allocation by 1.0M
words, promoted allocation by 0.2M words, and Cachegrind instructions by
0.1%.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Represent top-level, scoped, and map-reduce error continuations directly.
This removes closure wrappers from error contexts and accumulates map-reduce
errors through typed frames while retaining user-provided handlers as opaque
callbacks.

On a no-op @install self-build this reduced minor allocation by 0.43M
words and promoted allocation by 0.25M words, with a small Cachegrind
instruction reduction.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Separate error handlers from the hot continuation representation and store
map-reduce return state, handlers, and combiners in one context. Derive the
active collection handler from that context and queue captured errors directly
instead of reconstructing continuation jobs.

Across no-op self-build targets this reduced minor allocation by 0.55M to
0.89M words and promoted allocation by 0.37M to 0.63M words. Cachegrind
instructions fell by 0.8% to 1.4%, with neutral to improved native wall time.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Store parallel array results, counters, and the final continuation once per
map rather than repeating them in every child completion frame. Child frames
now contain only the shared state and their array index.

Across no-op self-build targets this reduced minor allocation by 0.32M to
0.58M words and promoted allocation by 0.05M to 0.12M words. Cachegrind
instructions fell by 0.04% to 0.10%, with neutral native wall time.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
Use a dedicated computation and completion frame for [collect_errors] instead
of constructing a generic map-reduce node followed by a map node. Caller
callbacks remain opaque and error collection still uses the existing scope
machinery.

Across no-op self-build targets this reduced minor allocation by 0.19M to
0.33M words and promoted allocation by 0.02M to 0.04M words. Cachegrind and
native wall time remained effectively neutral.

Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
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