PERF: release the GVL while the V8 thread boots the isolate#424
Open
ursm wants to merge 1 commit into
Open
Conversation
Context.new (multithreaded mode) spawns the V8 thread and then blocks on the late_init barrier while that thread runs v8::Isolate::New (snapshot deserialization) and safe-context setup. barrier_wait is a plain pthread_cond_wait, so the booting Ruby thread holds the GVL the entire time, stalling every other Ruby thread for the full boot. The V8 thread touches no Ruby objects during boot (the snapshot is a C buffer already copied into c->snapshot), so release the GVL around the barrier waits via rb_thread_call_without_gvl. A background thread booting contexts from a snapshot (e.g. a warm-context pool) now overlaps with the main thread instead of freezing it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In the default multithreaded mode,
Context.newspawns the V8 thread and then blocks on thelate_initbarrier while that thread runsv8::Isolate::New(snapshot deserialization) and the safe-context setup.barrier_waitis a plainpthread_cond_wait, so the booting Ruby thread holds the GVL for the entire boot, freezing every other Ruby thread for the full duration.The V8 thread touches no Ruby objects during boot — the snapshot is a plain C buffer already copied into
c->snapshot— so this change releases the GVL around the barrier waits viarb_thread_call_without_gvl(the same idiom already used byContext#dispose). A background thread booting contexts from a snapshot (e.g. a warm-context pool) now genuinely overlaps with the main thread instead of stalling it.Why it's safe
early_init→v8::Isolate::New/ context / safe-context setup →late_init) makes no Ruby calls and allocates no VALUEs beforelate_init, so the initializing thread never needed to hold the GVL there.self/cstay alive across the GVL-released wait via MRI's conservative machine-stack marking — identical to the existingrb_thread_call_without_gvl(context_dispose_do, c, NULL, NULL)inContext#dispose.context_markonly readsc->procs/c->exception, both initialized before the V8 thread exists and untouched during boot, so a concurrent GC mark is race-free.:single_threadedmode (which runs the boot inline on the Ruby thread) is unchanged.Measurement
Synthetic microbenchmark (background thread booting contexts from a heavy snapshot while the main thread runs a tight Ruby loop): on
main, every background boot stalls the main thread for the full ~8 ms deserialization; with this change those per-boot stalls disappear.Real-world A/B in a downstream app that maintains a warm
Contextpool with background async refill (real snapshot + pool + refill, median of 8 runs, identical workload): ~1.52s → ~1.40s wall, ≈ 8.5% faster.All existing tests pass (
rake test, 113 runs, 0 failures).🤖 Generated with Claude Code