Repository navigation
Conversation
Review of #1268 at 2cfa788 (base
|
The benches are down, so the batched draw's A/B and the lock census are owed rather than run: the frame allocator's record names the rows, benches and what main must show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 3 of #1268 at 1047f13 (base #1250 at 8245c9d; focused on round 2's points)ArchitectureNothing structural changed.
The PR's own net growth falls by 10 lines. Round-2 points
FindingsNone. Evidence
Since the specified head
LAND after #1250, once guest runs 43080189189 and 43081124750 pass. |
The frame allocator's record says the first decision is built here, #1265 having landed first, and names what main must show on metal for it: the benches are down, so the A/B and the lock census are owed rather than run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1268's rebase onto #1250 at 4e9f6f2 (main 8188095), at c00a628 (focused on the resolution)ArchitectureNothing structural changed since round 3's LAND. The rebase only resolves conflicts, plus the record update that round 3 suggested. The PR's diff over #1250 is +110/−39 lines over 5 files. The resolution
FindingsNone. One cosmetic point, optional: line 77 of the allocator record runs two wrapped lines into one long line ("…has to reach them. The second, …"). It renders the same. Evidence
LAND after #1250, once guest runs 43112778072 and 43112790573 pass. |
The frame allocator's record says the first decision is built here, #1265 having landed first, and names what main must show on metal for it: the benches are down, so the A/B and the lock census are owed rather than run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frame allocator's record says the first decision is built here, #1265 having landed first, and names what main must show on metal for it: the benches are down, so the A/B and the lock census are owed rather than run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frame allocator's record says the first decision is built here, #1265 having landed first, and names what main must show on metal for it: the benches are down, so the A/B and the lock census are owed rather than run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every frame a shared-memory region or a fault's populated run drew took the frame allocator's one lock on its own: 512 holds for a 2 MiB region, up to 16 for a fault. Under the build-shaped set at one child a CPU the lock was 99 % of every contended lock wait, its waits 1.05 of the T14's 8 CPUs and 7.1 of the AMD bench's 16 for the whole round, 82 % of the spin in the children's region creations and 13 to 14 % in their faults. `Frames::alloc_promised_each` draws `count` promised frames in one call, checking first that as many are promised; promises and ledgers move as for as many single draws. `pmm::alloc_promised_many` draws a region's zeroed frames and `pmm::alloc_promised_into` a run's unzeroed ones, a superframe's worth (512) to a hold, so a large region still lets other CPUs' draws in between. `SharedMemObject::create` takes a piece at a time, and a fault counts its run's empty pages, draws them together and writes each as its page starts (`fill_new`), which replaces `owned_frame` and `pmm::alloc_promised_unzeroed`. Frees already went back in batches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frame allocator's record says the first decision is built here, #1265 having landed first, and names what main must show on metal for it: the benches are down, so the A/B and the lock census are owed rather than run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stacked on #1250 (base
spawnperf, head e343eb5, on main 29c5f2d): this PR's diff is two commits, the batch29585b5 and the records commit 3eef788 (the batch built, and what it owes on metal). It is retargeted to main
once #1250 lands.
Problem
Every frame a shared-memory region or a fault's populated run drew took the frame allocator's one lock on its own:
512 holds for a 2 MiB region, up to 16 for a fault. The spawn diagnosis (#1265, boot 3) measured what that costs the
all-CPU build: with one build child a CPU the lock is 99 % of every contended lock wait, its waits 1.05 of the T14's 8
CPUs and 7.1 of the AMD bench's 16 for the whole round, 82 % of the spin in the children's
SHM_CREATEs (by inferencefrom the SDK, their 2 MiB directory windows) and 13 to 14 % in their faults. Lock spin is a third of the children's
growth in CPU on the T14 and 72 % on the AMD bench, so the gain expected on the T14 is the smaller.
Behavior
Frames::alloc_promised_each(count, each)drawscountpromised frames in one call, asserting first that as manyare promised (for the message); promises and ledgers move exactly as for as many
alloc_promiseds.pmm::alloc_promised_many(count)draws a region's zeroed frames andpmm::alloc_promised_into(slots)(mm's own,as unzeroed draws are since A spawn reserves its stack once, and a committed page's frame is written once #1250) a run's unzeroed ones, each at most a superframe's worth (512 frames) to a hold
of the lock, so a large region still lets other CPUs' draws in between. Both give their frames back through
free_drawnordiscard, as A spawn reserves its stack once, and a committed page's frame is written once #1250 states. Nothing allocates under the lock: the region's list is reserved first, a run's slotsare on the stack. Frames are zeroed after the lock is let go.
SharedMemObject::createtakes each piece in one call. A fault counts its run's empty pages, draws them togetherand writes each as its page starts (
fill_new, A spawn reserves its stack once, and a committed page's frame is written once #1250'sowned_framewithout its draw), which replacesowned_frameand
pmm::alloc_promised_unzeroed.free_all,free_drawn) and are unchanged. Per-CPU frame caches are held untilthis is measured (the orchestrator's decision, recorded in issues: the all-CPU build waits on the frame allocator's lock, for the 2 MiB windows #1265). When a region's frames are drawn at first touch
(the next PR),
alloc_promised_manyloses its region caller; the fault's run keeps the batch.No loom or concurrency model: every draw and free still happens under the one allocator lock, as before, so a batch
meets concurrent frees exactly as consecutive single draws did. The lock-free part, the pin counts, decides only which
freed frames return to the free map (
Pins::defer,release), which a draw never reads.Checks
Landing evidence: one run at the stack's top, 43173093523 at A shared-memory region draws each page's frame at its first touch #1270's head e37586c (which contains this PR's
head), pending:
testcases_members_exit_0andshared_debug_members_exit_0(every shared member,abuse_spawn_mapsamong them) and
a_spawn_from_an_image_frees_it_when_the_child_ends,osbench_capability_groups_exit_0,virt_user_mode,virt_smp. The earlier row runs no longer cover the landed heads.Rebased onto main 29c5f2d (2026-11-12), with A spawn reserves its stack once, and a committed page's frame is written once #1250's member fix e343eb5:
abuse_spawn_maps's past-the-objectrange names
backing: SpawnBacking::Copy, the field A memory object's range is mapped where the caller says, and an immutable one is shared and executable: /system text is ROOT's own pages #1259 added after the case was written (no earlier runcompiled it: it is a shared member). At each head the testcases image builds,
vm::andframes::pass and thekernel's clippy shapes exit 0; every guest member compiles (
rebase5-checks.login the evidence).Rebased onto main 3c5c555 (after A switch loads only what the incoming thread does not already have #1251 and The shared boots run as guest rows, each member judged by its own exit #1271, 2026-11-12) at 5931031: comment-only. A switch loads only what the incoming thread does not already have #1251 added "no CPU holds
its root" to the doc on
impl Drop for AddressSpace, which A spawn reserves its stack once, and a committed page's frame is written once #1250 rewrote; both stand. Every other commit isunchanged (
rebase4-range-diff.txtin the evidence).Rebased onto main a7ca8ad (after An unmap walks no futex bucket while no thread of its space is in a futex wait #1223 and A poll belongs to the handle it was made through, and a ring to its one submitter #1261, 2026-11-12) at c2e8b9f. No conflict of its own; its
commits are unchanged, on A spawn reserves its stack once, and a committed page's frame is written once #1250's new head. At c2e8b9f:
vm::andframes::exit 0, kernel clippy for
x86_64-unknown-none, withboot-actuators,test-actuators, foraarch64-unknown-none-softfloatand the library's tests, exit 0 each (rebase3-*in the evidence).Rebased onto main 75bdec3 (after A memory object's range is mapped where the caller says, and an immutable one is shared and executable: /system text is ROOT's own pages #1259, 2026-11-12). The one conflict was
kernel/pure/vm.rs's tests: A memory object's range is mapped where the caller says, and an immutable one is shared and executable: /system text is ROOT's own pages #1259'san_object_maps_as_what_it_isand this stack's copied-page tests both stay. At e23c2d6:vm::andframes::exit 0,kernel clippy for
x86_64-unknown-none, withboot-actuators,test-actuators, foraarch64-unknown-none-softfloatand the library's tests, exit 0 each.
Targeted (kernel
frames,mm,object::shm):At 1047f13 (review r1: docs, and the test of the up-front check deleted, since a draw past the promises already
panics):
cargo test -p kernel --lib --features sched-check frames::exit 0 (40 passed,a_batch_of_promised_frames_draws_each_promise_onceamong them) andvm::exit 0 (16 passed); clippy with theadopted lints and
-D warningsof the library's tests and of the kernel forx86_64-unknown-none, exit 0 each.At 2cfa788 (the same code but for those docs, that test and A spawn reserves its stack once, and a committed page's frame is written once #1250's r2 docs and test table): clippy, exit 0 each,
for the kernel with
boot-actuators,test-actuatorsand foraarch64-unknown-none-softfloat.Rebased with A spawn reserves its stack once, and a committed page's frame is written once #1250 onto main 8188095 (2026-11-12) at c00a628. Conflicts:
kernel/src/mm/space.rs, where main'spopulate fills a run in a restructured arm (A paged read takes two passes of its pager's lock and populates nothing ahead #1245); the batched draw of a private or copied run moves into it as it
was. And the frame allocator's record, which issues: the all-CPU build waits on the frame allocator's lock, for the 2 MiB windows #1265 rewrote: it now says the batched draw is built (issues: the all-CPU build waits on the frame allocator's lock, for the 2 MiB windows #1265 landed
first), drops the names of code this changes, and keeps the owed bullet. At c00a628:
vm::exit 0 (16 passed),frames::exit 0 (40 passed), kernel clippy forx86_64-unknown-none, withboot-actuators,test-actuators, foraarch64-unknown-none-softfloatand the library's tests, exit 0 each.Guest rows, run 43135817474 at c2e8b9f (code-identical to 5931031, which a comment-only rebase made; the old head is kept on
hold-*), pending: x86a_spawn_from_an_image_frees_it_when_the_child_endsandosbench_capability_groups_exit_0, aarch64virt_user_modeandvirt_smp(the fault path and region creation areshared by both architectures and under every program). (43081124750 named a shared-boot member and failed before
running; 43110114582 at the pre-rebase head was cancelled unrun.)
Evidence: https://github.com/ToyOSOrg/evidence/tree/804cbbf0148a7b0b0b67ac421bb795035ed742d6/pmm-batch
Metal (owed, not run)
The benches are down, so this lands on review, the host checks and the guest rows, and the speed-up is not yet
measured on metal. Owed on metal, recorded in the frame allocator's record:
osbench work(work.build_shaped.j1, and.j8on the T14 and.j16on the AMD bench),spawn_costandosbench procs: main after must showwork.build_shaped.j<cpus>faster on both benches and noj1orprocrow slower beyond the before run's spread;spawnperf-split, carried onto main after):the allocator lock's share of contended waits, 99 % before, fallen on both.
The A/B that was staged (
request5.txtin the evidence) stays as the record; its images are deleted.Risks
costs is not measured. A region of 2 MiB or less takes one hold.
🤖 Generated with Claude Code