rpc: parallelize cached tensor hashing during model load - #26291
Conversation
|
Hi @chuyqa, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
Tested on the 96-core Arm box from #25890. It works: 1.55x here, and it moves the bottleneck to the server. Two nodes, 96-core Arm Neoverse-N2, 672 GB each, connected at 100 GbE and comfortably capable of 70+ Gbps sustained node-to-node. GLM-4.5-Air Q4_K_M (68 GB),
No regression with the env var unset. It also helps with no cache at all (86 to 66 s), since the client hashes before the server can answer "miss", so that is a broader win than the description suggests. Sampling Worth noting the loader is single-threaded regardless of RPC: same box, no Happy to share the per-core traces or screenshots if useful, and to re-run on a larger model. |
|
After looking more at the server-side bottleneck, I think the client-side load changes in this PR may be better kept separate from a larger RPC/server rework. The current change is self-contained:
The server side changes are heavier and after a few attempts I see it as requiring:
This feels like a heavier/follow up PR work effort. One question for this PR @ggerganov : should the load worker count be a new env var like |
|
Scope split sounds right. The client side win stands on its own and the server phase is a separable problem. On the env var question: a dedicated variable is better. Load parallelism wants as many threads as I/O allows, while One datapoint for the follow-up's motivation: this week we loaded a 1.34TB model (plain mmap, no RPC) on a 96-core box and the load sat at ~92% of one core for 9+ minutes with the disk mostly idle. At that scale the single threaded phase dominates time to first token regardless of transport. Happy to benchmark the server side follow-up on the same hardware class when it exists. |
|
Worked for me. Super helpful in iterating llama build changes. From my agent who did all the lift (gpt 5.6 sol): Community QA datapoint: I manually ported the functional changes from Setup:
I ran a cache-disabled A-B-A-B comparison using the same client/server package and launcher configuration, changing only
The serial path averaged 754.524 s and the 8-worker path averaged 539.326 s, saving 215.198 s (28.5%, or about 3m35s per restart). The controls differed by only 0.077 s, and the A-B-A-B ordering makes filesystem warming an unlikely explanation. The unset/default path reached healthy twice, so I did not observe a startup regression. Both threaded starts also reached healthy without client or RPC errors. After each enabled run, our normal smoke checks passed: vision OCR, exact post-image arithmetic, structured tool calling, and MTP drafting/acceptance. Prompt/decode performance remained in its expected range. I also tested a populated RPC tensor cache:
We retained 12 workers in production since 16 was effectively tied. Warm production restarts now average about 5m20s, versus roughly 12m35s for the earlier serial/cache-off setup. That overall reduction includes the cache benefit, while the cache-disabled 28.5% result is the cleaner measurement of this change itself. This definitely addressed the long-load interruption that motivated us to try it. Happy to provide the raw timing or smoke-test logs if useful. |
|
@rgerganov looking for feedback |
|
hi can you also check this issues too #27585 edit |
|
Now that PR #18626 has merged, the mutex safe workaround in ggml-rpc.cpp shouldnt be needed anymore. I'll need to test the existing PR's llama-loader changes, as they should still be applicable: The loader kicking off 12 |
a7cf96a to
14b1e9e
Compare
|
Retested after #18626 on master @ bf94216, with all ggml-rpc.cpp changes removed from #26291 Model: Kimi-K2.7-Code UD-IQ2_M Polling /models every 0.25 sec until "loaded":
|
Overview
Related to #25890.
Up to 20-60% lower RPC model load time by adding GGML_RPC_LOAD_THREADS.
On RPC-cache loads, the client fully FNV-hashes each large RPC tensor one at a time before dispatching it. For large models, this can pin a single CPU core at 100% while the remaining cores sit mostly idle.
This change:
Additional information
Main host: Ryzen 9 3900X, 128 GB RAM, 2× RTX 4060 Ti 16 GB,
RPC host: Ryzen 5 9600X, 192 GB RAM, 1× RTX 4060 Ti 16 GB
Connected via 2.5gbps peer to peer lan.
Model: Kimi-K2.7-Code UD-IQ2_M, 318 GiB, 8 files
RPC CPU allocation: approximately 143 GiB
GGML_RPC_LOAD_THREADS=12
On this very limited hardware, we see a near 3x improvement to the loading phase:
On b10173 - "state":"loading" 4min54sec
With this PR and GGML_RPC_LOAD_THREADS 12 - "state":"loading" 1min38sec
With this PR and GGML_RPC_LOAD_THREADS not set - "state":"loading" 4min47sec
Test the existing code path to ensure we dont regress
Requirements
YES.
Grok to find the problem / single threaded slowdown.
GPT to find a solution
Grok to refine the solution
GPT to optimize and test.