贡献 / #1838
#1838 Resident RAM mode: the RAM copy follows the conversation (`STRATA_RAM_ADAPT`, opt-in)
open · @SanghunYun · 0 评论 · 去 GitHub 看
BenchmarksServer & APIMulti-GPUAMD / HIPNVIDIA / CUDAModels & quantsDocumentationWindowsLinux
说明
## Title Issue: Resolves #1837 ## Summary In the resident RAM mode the RAM copy is chosen once, at start. On a PC whose RAM cannot hold every expert the GPU does not, an expert outside the copy is read through the file tier when needed, but is not retained in the resident RAM copy. Read buffers or the OS cache may hold its bytes; the adaptive tier exchanges only the resident copy and VRAM. `STRATA_RAM_ADAPT=<n>` (opt-in) lets the resident RAM copy follow the conversation as the VRAM tier does. Rebased onto **v0.1.43 (`7695859a`)** and measured again on RTX 3060 12 GB / 32 GB RAM / IQ2_XS, with `--adapt-async 1 --spec 3` and the release's default tail skip (7). 47 recorded Korean requests from my own use (tools, follow-ups and Open WebUI background tasks) were frozen and replayed in order: release → option on → release, each in a fresh engine starting from the same held learned profile. The prompts are private and are not attached. These are total tokens / total engine time, compared with the **closing release**, not medians of three runs per arm: | actual new-input group | v0.1.43 decode tok/s | `STRATA_RAM_ADAPT=8` | gain | |---|---:|---:|---:| | all 47 requests | 43.26 | 50.40 | **+16.5%** | | short (<2,000 new tokens), 39 requests | 43.36 | 50.41 | **+16.3%** | | long (≥2,000 new tokens), 8 requests | 43.09 | 50.39 | **+16.9%** | The two release arms decode at 43.33 and 43.26 tok/s (0.16% apart). Prompt speed differs by 2.5% overall, within run variation; short/long prompt speed differs by +4.0% / +0.6%. Decode demand file-tier blob reads fall from **14.15 → 2.67 per generated token**. The 8,056 RAM promotions read another blob each: **3.16 per token with those added**, a 77.7% reduction. These counts are not physical SSD I/O. **With tail skip.** RAM residency does not exempt an expert from being skipped. This option keeps needed experts' original bytes in RAM instead of reading them through the file tier again; it does not change the routing or the VRAM tier's rule. The gain above is with v0.1.43's default tail skip. Tail skip off was not retested on v0.1.43. The previous v0.1.42 and v0.1.41 measurements remain below, labelled with their releases and workloads. **How it works** 1. **When:** before a decode window, while no adaptive round is in flight (not in a request's first window). 2. **What it reads:** the adaptive tier's own decayed routing counts. Nothing new is counted. 3. **The choice:** per layer, the file-tier expert with the highest count (2 or more, the tier's candidate floor) and the RAM expert with the lowest (below 0.5). The pairs with the largest difference first, at most `n` per window. 4. **The move:** the incoming blob is read from the file beside the copy, then written into the outgoing expert's place. Experts of one layer have one size, so the RAM stays the same size. The outgoing expert is on the file tier again and can come back the same way. The routing, the weights and the VRAM tier's rule are untouched. **Checked** - **Off = main.** With the variable unset: the same 4,489-token prompt, 256 new tokens, greedy, the blocking tier, `STRATA_IQ_MT_MIN=1`, `STRATA_PREFILL_CPU_SHARE=0`, a fresh engine each time, v0.1.43 / this PR off / v0.1.43 - three answers, one SHA-256. (On v0.1.41: four answers, one SHA-256.) - **Unit test** (`file_expert_source_test --rotation-gpu`, pinned and pageable): the moved-in bytes are the file's, the moved-out expert is read from the file again, a refused call changes nothing, and an exchange with the VRAM tier still works after a move. - **Soak** (on v0.1.41, not repeated): 61 minutes back to back with the option on - 140 requests, none failed, 22,065 experts moved, decode 45.1-46.4 tok/s by ten-minute slice, engine RSS 23.65 -> 23.68 GiB, no error line. - **v0.1.43 replay:** 141 requests across the three speed arms and three off-identity requests, none failed. - **Earlier v0.1.42:** 12 runs, 48 requests, none failed. (The 24 runs on v0.1.41: 96 requests, none failed.) ## What changed - `include/strata/core/expert_source.hpp`, `src/core/expert_source.cpp`: `FileExpertSource::replace_resident(layer, in, out)`. It refuses when `out` is not in the copy, `in` already is, an exchange is staged, either has an exchange override, or `STRATA_EXCHANGE_ROTATE` is active; a failed read leaves the copy as it was. `ram_adapts()` counts the moves. - `src/program/generate.cpp`: the choice above, in the serial `--serve` decode loop before `adapt_tick`, and one line in the per-request stats once an expert has moved. It does nothing with a layer split, a peer GPU, a helper cache or batch slots. - `tests/core/file_expert_source_test.cpp`: `test_replace_resident`. - `docs/DETAILS.md`: the option, beside `--adapt-async`. Left alone: the routing, the weights, the VRAM tier's rule, the expert profile and `--expert-profile-save`, the prompt path (its routing is not counted; a later prompt reads from the RAM copy as decode left it), every default. ## Extra Notes **Not tested / limits** - Built and run on CUDA/Linux. This head also builds and links on HIP (ROCm 7.10.0a20251120, gfx1100, Ubuntu 26.04); the default host-side `file_expert_source_test` passed with GPU access disabled. AMD model inference and `--rotation-gpu` were not run on HIP. Windows and SYCL were not built. SYCL selects its own copies of the modified engine sources and header, so this change does not add RAM adaptation to the SYCL path. - One machine, one quantization. With every expert in RAM or VRAM there is no file-tier candidate, so nothing moves. - The two thresholds (2 and 0.5) were not tuned; `n` = 8 and 32 measured the same on my own workload (v0.1.41). - On v0.1.43, performance was measured only with `--adapt-async 1 --spec 3`, default tail skip and the private Korean replay above. Tail skip off, the blocking tier, `--spec 2`, the public four-language benchmark and the soak were not repeated on v0.1.43. The #1324 comparison is from earlier releases. The earlier gains are not directly divided by these numbers, since the workloads and starting profiles differ. - Not every individual request improved: the median request-level decode gain is 14.9%; 41 requests improved by more than 3%, five were within ±3%, and the initial 12-token background task was 3.2% slower. No short/long aggregate section worsened in this run. - Each move is a blocking read of one blob (about 0.4 ms in the earlier v0.1.41 measurement) on the decode thread, at most `n` per window. - `--pipeline-windows 2` runs another loop, which this PR does not touch. - Answers were not graded. The moved bytes are the file's. <details> <summary><b>Machine and settings</b></summary> RTX 3060 12 GB connected over OCuLink (PCIe 4.0 x4, 170 W limit); Ryzen 7 7840HS engineering sample (8 cores / 16 threads, reported as `AMD Eng Sample: 100-000000964-50_Y`, 3.5 GHz maximum); 32 GB LPDDR5-6400; Samsung PM981 512 GB NVMe (MZVLB512HAJQ, PCIe 3.0 x4). Ubuntu 26.04, kernel 7.0, on the power-saver profile (`amd-pstate-epp`, `powersave` governor, EPP `power`) for every run of both arms; driver 615.78, CUDA 13.4, source build (`STRATA_NATIVE_EXPERTS`, `STRATA_MMQ_KQUANTS`, sm_86). Model: ISTA-DASLab Qwen3.8-Flash-Next GSQ-RCO IQ2_XS (revision ed59f92), for the earlier v0.1.41/v0.1.42 measurements, the repository's `data/expert-profile.bin`, no learned profile. 20.5 GiB of experts in RAM, 3,968 expert slots in VRAM (3,970 on v0.1.41), about 6,000 experts on the file tier. The earlier v0.1.41/v0.1.42 settings, both arms: `--expert-cache 4200 --resident-budget-gib 20.5 --mmap-experts --kv k8v4 --max-context 131072 --prefill auto:16384 --pool-workers 7 --ple-io direct --greedy --vision --prompt-cache 2 --conversation-cache-mib 1536 --conversation-cache-slots 3`, `STRATA_UNBUFFERED_LOAD=1`, `STRATA_STAGER_THREADS=8`, `STRATA_LOOKAHEAD=1`, `STRATA_PREFILL_CPU_SHARE=auto`, and the adaptive tier and verify window of the table's row (`--spec 3` is `--spec 3 --mtp-max-t 3 --spec-min-p 0.5`; `--spec 2` is `--spec 2 --mtp-max-t 2`). The timing variables `STRATA_PREFILL_TIMING`, `STRATA_VERIFY_PROFILE` and `STRATA_DECODE_TIMING` were set in both arms. The only difference between the arms is the binary and `STRATA_RAM_ADAPT=8`. The v0.1.42 runs: `--adapt-async 1`, `--spec 3` only, with `--suffix-draft 0 --vram-reserve-mib 500`; `STRATA_ROUTE_TAIL_SKIP` unset (so 7) or `0`, as the row says; `--pcie-frac` unset, and 0.1.42's own measurement settled on 0.15 in every run of every arm. </details> <details> <summary><b>v0.1.43: workload, cache state and every arm</b></summary> Actual prompt contexts are 281-33,241 tokens. All 47 model-input token-ID hashes match across arms. The closing release and option-on arm read 75,917 tokens and reused 375,160; two requests' read counters differed by one token each, with the same totals. The opening release read 88,847 and reused 362,230 because two requests restored less of their prefix; its aggregate prompt speed is not directly compared with the option-on arm. | arm (execution order) | generated tokens | decode tok/s | read / reused | prompt tok/s | |---|---:|---:|---:|---:| | release-before | 16,598 | 43.33 | 88,847 / 362,230 | 327.31 | | option-on | 16,293 | 50.40 | 75,917 / 375,160 | 313.09 | | release-after | 17,175 | 43.26 | 75,917 / 375,160 | 305.35 | Output lengths differ, so whole-run elapsed-time ratios are not used as speed gains. Each arm has 47 API requests and 56 engine generation segments including recoveries. The same local OpenAI-compatible server and engine DONE timing fields were used on both sides. No requests were graded. 3,968 actual expert slots, resident budget 20.5 GiB, K8V4, prefill auto:16384, `--spec 3 --mtp-max-t 3 --spec-min-p 0.5`, `--suffix-draft 0 --pool-workers 7 --vram-reserve-mib 500`, CPU prefill share auto, `STRATA_UNBUFFERED_LOAD=1`. `STRATA_PREFILL_TIMING` and `STRATA_DECODE_TIMING` are on in both arms; the GPU verify profiler is off in both. The same learned starting profile is restored before every arm and restored/released after the experiment; the live profile does not retain learning from the test requests. The release's PCIe auto settles on 0.15. Prompt cache=2, conversation cache=1,536 MiB/3 slots, minimum free=1,280 MiB. Each arm is a fresh process; the OS page cache is not forcibly cleared. Vision is configured on-demand on the separate AMD/Vulkan device, but no images were used. The 131,072 context setting is not a claim that 128K input was tested. The initial storage load is included in the first request, so excluding it the engine process reads 537.7 → 329.1 GB from storage across the remaining requests (decimal GB). This includes prompt, decode, RAM moves and other engine I/O, not decode expert I/O alone. Demand decode reads are 243,086 → 43,422; RAM promotions are 0 → 8,056 and are counted separately. v0.1.43's adaptive overlap does not run with `--adapt-async 1` and is also off by default in resident RAM mode. Overlap-on, Windows, AMD GPU inference, multi-GPU, pipeline/batch and long-running soak were not tested here. The existing memory guard stayed active; no guard limit was violated. Config and learned profile were restored and serving was left stopped after the tests. </details> <details> <summary><b>v0.1.42: previous headline and language transition</b></summary> `STRATA_RAM_ADAPT=<n>` (opt-in) lets the RAM copy follow the conversation as the VRAM tier does. RTX 3060 12 GB, 32 GB of RAM, IQ2_XS, measured again against v0.1.42 (`61b3fb5`), `--adapt-async 1`, `--spec 3`; decode tok/s, median of three fresh-process runs per arm: | route tail skip | v0.1.42 | `STRATA_RAM_ADAPT=8` | | |---|---:|---:|---:| | 7 (the 0.1.42 default) | 45.19 | 49.51 | **+9.6%** | | off (`STRATA_ROUTE_TAIL_SKIP=0`) | 35.36 | 40.40 | **+14.3%** | Prompt speed stays within run variation (466-471 tok/s without, 470-472 with). Blob reads from the file fall from 41.9K to 21.1K per run (68.7K to 33.9K with tail
本站相关内容
相关页面的快捷入口。