feat: Add NUMA-aware RAM-disk streaming - #377
Conversation
|
Heads-up: |
Thanks, ill correct that before i take it out of draft |
6ab39e7 to
42e44d3
Compare
Retarget PR JustVugg#377 onto current dev (origin/dev @ 4aca059) after JustVugg#391 split glm.c into colibri.c + quant.h/sample.h/kv_persist.h/telemetry.h. Reconciles four collisions: - JustVugg#391 split: all glm.c hunks resited -- prof_*/g_prof_io/ProfBase live in colibri.c (NOT telemetry.h); tiers_emit/emap_emit -> telemetry.h (with a rammap_slot forward-decl); st_fd_is_tmpfs/st_fd_fs_magic -> st.h; .coli_kv state-dir -> serve_ctx_init/run_serve/run_serve_mux + main. - DISK-CLASS (1f00142): prof_physical_read_bytes/ProfPhysicalWire merged ON TOP of dev's dc_* fields; PROF protocol line extended 9->17 fields additively; expert_load_impl 6-arg `demand` signature preserved; g_prof_io routed through prof_ssd_tensor_bytes (tmpfs-excluded). - DUAL-SSD mirror (JustVugg#298/JustVugg#469): ESlot.backing hand-merged with dev's aslab/afslab; map_of_fd exact-length + MADV_HUGEPAGE composes with rep_bfd. - int3 fmt=5 (JustVugg#168): rammap_bind_one reuses dev's qt_resolve_fmt/detect_group_size (inline fmt-detection dropped; duplicate detect_group_size not re-added). Verified: make colibri 0 warnings; make check 187 tests pass (test_uring skips in sandboxes via the PR's helpers; test_rammap builds against colibri.c and passes). Default path byte-identical -- oracle-safe by construction. Co-Authored-By: Claude <noreply@anthropic.com>
|
@JustVugg Rebase complete . should be good to go. |
Real-world validation and measured benefitTested at commit Host and model
Exact I/O commands and run policymake -C c iobench
MODEL=/run/media/ben/Storage/glm52_i4
# Physical SSD: O_DIRECT, no warm-up, three measured runs.
./c/iobench "$MODEL/out-00069.safetensors" 19 64 8 1
# Warm page cache: one warm-up, then three measured runs.
./c/iobench "$MODEL/out-00069.safetensors" 19 64 8 0
# tmpfs: copy the same shard, one warm-up, then three measured runs.
TMP=$(mktemp -d /dev/shm/colibri-pr377.XXXXXX)
cp --reflink=never "$MODEL/out-00069.safetensors" "$TMP/"
./c/iobench "$TMP/out-00069.safetensors" 19 64 8 0
rm "$TMP/out-00069.safetensors"
rmdir "$TMP"Each measured run performs 64 deterministic random 19 MiB reads with eight threads (about 1.3 GB read per run).
On this host, tmpfs delivered 2.13Γ the median physical-SSD throughput and about 53% lower effective block latency. It was only about 1.06Γ faster than an already-warm page cache, which is an important caveat: this is an I/O-path measurement, not a claim of a 2.13Γ token-rate improvement. The practical benefit is making selected weights predictably memory-resident and eligible for direct mapping instead of depending on cache residency and repeated SSD reads. The temporary 2.69 GB tmpfs copy was removed after the measurements. Production planning against the real modelCommands: python3 c/coli ramdisk plan \
--model /run/media/ben/Storage/glm52_i4 \
--json
python3 c/coli ramdisk plan \
--model /run/media/ben/Storage/glm52_i4 \
--mode partial \
--capacity-gb 8 \
--profile /run/media/ben/Storage/glm52_i4/.coli_usage \
--jsonFull mode identified all 144 shards and calculated 429,249,168,387 bytes of required staging/runtime/OS reserve against about 20.2 GB available. It correctly refused preparation with:
The 8 GiB partial plan selected three complete shard closures (8,058,234,192 bytes). From the real usage profile, the planner estimated 43,108,032,512 bytes of expert traffic avoided (5.35 predicted bytes avoided per staged byte), but only 2.81% profile coverage versus a 30.01% same-budget hot-expert No reserve override, Regression validationenv PYTHONDONTWRITEBYTECODE=1 make -C c test-python
make -C c test-c
The opt-in host mount lifecycle could not be rerun through this automation session because sudo requires interactive authentication and this kernel disallows the isolated user-namespace mount. The earlier real-tmpfs lifecycle result remains documented in the PR, but I am not presenting it as a fresh run at this merge commit. Remaining hardware acceptanceA full engine-level SSD/slab/direct-map token-throughput A/B still needs a host with enough free memory for the planner's reserve (about 400 GiB for this snapshot in full mode, or at least 52.8 GB for this specific 8 GiB partial benchmark plan). That run should use |
|
Sorry for the wait on the workflow approval β that was on me, not you. GitHub holds runs from contributors whose first PR hasn't merged yet, and I hadn't noticed how many were queued. Approved now, and CI has spoken:
Both failures are in the new Windows β macOS β Nothing here touches the substance of the PR, which is large (+7,717) and which I still owe a proper read. Fix the two platform issues and I'll go through it. And since |
|
Follow-up: this now also conflicts with Recap of where it stands, so nothing is ambiguous:
The macOS one is worth a second look beyond the test: your symlink guard is behaving correctly, but a real Mac user pointing state anywhere under Nothing here touches the substance, which I still owe a proper read β it's +7,700 lines and I want to give it real attention rather than a skim. Rebase, fix the two platform issues, and I'll go through it. Since |
45acdf7 to
cdaf08c
Compare
Retarget PR JustVugg#377 onto current dev (origin/dev @ 4aca059) after JustVugg#391 split glm.c into colibri.c + quant.h/sample.h/kv_persist.h/telemetry.h. Reconciles four collisions: - JustVugg#391 split: all glm.c hunks resited -- prof_*/g_prof_io/ProfBase live in colibri.c (NOT telemetry.h); tiers_emit/emap_emit -> telemetry.h (with a rammap_slot forward-decl); st_fd_is_tmpfs/st_fd_fs_magic -> st.h; .coli_kv state-dir -> serve_ctx_init/run_serve/run_serve_mux + main. - DISK-CLASS (1f00142): prof_physical_read_bytes/ProfPhysicalWire merged ON TOP of dev's dc_* fields; PROF protocol line extended 9->17 fields additively; expert_load_impl 6-arg `demand` signature preserved; g_prof_io routed through prof_ssd_tensor_bytes (tmpfs-excluded). - DUAL-SSD mirror (JustVugg#298/JustVugg#469): ESlot.backing hand-merged with dev's aslab/afslab; map_of_fd exact-length + MADV_HUGEPAGE composes with rep_bfd. - int3 fmt=5 (JustVugg#168): rammap_bind_one reuses dev's qt_resolve_fmt/detect_group_size (inline fmt-detection dropped; duplicate detect_group_size not re-added). Verified: make colibri 0 warnings; make check 187 tests pass (test_uring skips in sandboxes via the PR's helpers; test_rammap builds against colibri.c and passes). Default path byte-identical -- oracle-safe by construction. Co-Authored-By: Claude <noreply@anthropic.com>
|
@JustVugg Thanks for the detailed recap β Iβve addressed each item on the current pushed head (
Validation at this head: local One GitHub-side item remains: the new |
|
I refactored the tui and upgraded the aesthetic and ease of use. Right now this feature is only supported on linux. subsequent updates will include macos and windows. |
|
|
@JustVugg , It has been rebased. |
|
Rebase request β and an apology for how many of these you have had.
Before asking, three things changed on our side so that this is the last one of these you should need for a while. 1. The main source of these conflicts is closed. Gates are now derived from the build rules (#733). Adding a test means adding your 2. We resolved what we could ourselves instead of asking. Eight PRs were unblocked by a maintainer pushing the merge to the contributor's branch rather than requesting a rebase β including three where both sides carried a real change and had to be merged rather than picked. No commits were rewritten. Yours is here because its conflict is in engine code ( 3. Merge order is oldest-clean-first from now on. A PR that is green and unconflicted merges ahead of anything opened after it. The reason some of you rebased many times is that newer, smaller PRs kept jumping the queue and resetting you β a starvation loop we built, not bad luck on your side. What we need: one rebase onto current If you would rather not, say so and we will close it with thanks and the branch stays yours to reopen. No pressure either way, and no hard feelings β several of these have been open a while through no fault of the author. |
Retarget PR JustVugg#377 onto current dev (origin/dev @ 4aca059) after JustVugg#391 split glm.c into colibri.c + quant.h/sample.h/kv_persist.h/telemetry.h. Reconciles four collisions: - JustVugg#391 split: all glm.c hunks resited -- prof_*/g_prof_io/ProfBase live in colibri.c (NOT telemetry.h); tiers_emit/emap_emit -> telemetry.h (with a rammap_slot forward-decl); st_fd_is_tmpfs/st_fd_fs_magic -> st.h; .coli_kv state-dir -> serve_ctx_init/run_serve/run_serve_mux + main. - DISK-CLASS (1f00142): prof_physical_read_bytes/ProfPhysicalWire merged ON TOP of dev's dc_* fields; PROF protocol line extended 9->17 fields additively; expert_load_impl 6-arg `demand` signature preserved; g_prof_io routed through prof_ssd_tensor_bytes (tmpfs-excluded). - DUAL-SSD mirror (JustVugg#298/JustVugg#469): ESlot.backing hand-merged with dev's aslab/afslab; map_of_fd exact-length + MADV_HUGEPAGE composes with rep_bfd. - int3 fmt=5 (JustVugg#168): rammap_bind_one reuses dev's qt_resolve_fmt/detect_group_size (inline fmt-detection dropped; duplicate detect_group_size not re-added). Verified: make colibri 0 warnings; make check 187 tests pass (test_uring skips in sandboxes via the PR's helpers; test_rammap builds against colibri.c and passes). Default path byte-identical -- oracle-safe by construction. Co-Authored-By: Claude <noreply@anthropic.com>
fd27698 to
07c2420
Compare
The only conflicting hunk was .gitignore, where this branch adds test binary names and dev added .claude/ (Claude Code's worktree directory). Both are additive lists, so both are kept -- picking a side would have un-ignored somebody's build artifacts. Resolved by a maintainer rather than asking for another rebase: this branch was already rebased onto current dev earlier today, and the remaining collision was a line dev added afterwards. All four engines build after the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thank you for rebasing this on 1 August β you were the only author of the eleven we asked who came back the same day, and it is noticed. The remaining CI has now run on this branch for the first time. That is worth stating plainly, because it explains part of why this has sat: GitHub holds workflows for first-time contributors until a maintainer approves them, and nobody had. Entirely our fault, and I have approved them. The result, and it needs your attentionplus, earlier in the same run: The failing test is one of yours β the Windows-portability facade β and the None of that is a judgement on the idea. It is the first CI feedback this branch has ever had, and it arrived two weeks late through no fault of yours. The real conversation: this is a project inside a PR+38,351 lines across 87 files. The breakdown:
For scale, that Python subsystem is larger than three of our four engines put together, and I am not going to merge that in one piece, and I do not think you would want me to. Not because of the size in itself, but because a 2,305-line interactive curses UI is a permanent maintenance commitment for a project whose selling point is that one person can read the whole engine. If it breaks in six months, who fixes it? What I would like instead: three PRs1. The engine side ( 2. Planning and mounts β the logic that decides what goes where and manages the mount lifecycle. Reviewable, testable, useful without any UI. 3. The curses UI and the benchmark harness β separately, or in a companion repo. Honest question rather than a rejection: does the RAM-disk need an interactive TUI to be useful, or would Split that way, (1) can be merged while (2) is reviewed and (3) is discussed. As one PR, all three wait for the slowest part. One design question, asked in good faithIf a machine has enough RAM to hold the model in a RAM-disk, it also has enough to hold the experts resident β and So what I want to understand from your numbers is where the RAM-disk wins over simply pinning more. My guess is that the answer is in the NUMA half of your title rather than the RAM-disk half β pinning does not let you choose which memory controller β and if that is right, then the NUMA placement is the valuable contribution and the RAM-disk is the mechanism you used to get it. If you have a measurement that separates the two, that is the thing that would make the case here, and it is much cheaper for you to produce than for us. No deadline, and please do not read the size discussion as the work being unwelcome. Nobody else has attacked the NUMA problem, you responded to a rebase request within hours, and you are working on multi-socket hardware we do not have. |
Summary
coli ramdiskTUI and equivalent scriptableplan,prepare,status,benchmark,start,stop, anddestroycommandsO_DIRECT, pipeline, and io_uring paths for SSD fallbackMotivation
Large MoE models are frequently limited by repeated expert reads from SSD. This adds a managed tmpfs tier that can stage all weights, or only profile-selected shard closures, while letting eligible experts bypass slab allocation and LRU I/O through read-only mappings. Unstaged experts continue to use Colibri's existing SSD streaming path.
The canonical model is never modified. Managed state stays on durable storage, and the implementation does not change global swap settings or HugeTLB reservations.
Safety and compatibility
COLI_RAMMAP=1and legacyCOLI_MMAP=1are mutually exclusiveValidation
make checkβ portable build, full C suite, and 141 Python tests passed with zero compiler warnings; one privileged integration remains opt-in in the default suiteprepare/status/destroyintegration passed, including no swap growth and durable KV preservationgit diff --check origin/dev...HEADpassedorigin/dev; upstream storage-read, sampling, filesystem-detection, Windows, Nix, and documentation changes remain covered by their testsHardware follow-up
Full 744B-model token parity, multi-node placement/throughput, and full/partial physical SSD-read acceptance still require a suitably provisioned NUMA host and model fixture.