Repository navigation
--aux-cpus: helper threads on spare CPUs, +10% decode on a layer split (Linux, opt-in) - #1598
Closed
noon-at-cgn wants to merge 2 commits into
Closed
noon-at-cgn wants to merge 2 commits into
noon-at-cgn wants to merge 2 commits into
Conversation
…r the host on spare CPUs (Linux, off by default) Header-only placement (include/strata/platform/aux_cpus.hpp): the spare CPU set (the host core's SMT siblings, then CPUs of physical cores with no worker; never a CPU that shares a core with a worker), the list parser, threads the engine creates pin themselves, threads somebody else created (the CUDA driver's) are moved by a sweep over /proc/self/task after the pool exists and at every request; pool workers and the host thread are never moved. --aux-cpus off|auto|LIST / STRATA_AUX_CPUS; per request strata_tune aux_cpus=0|1 (serve/server.py). Default off: the hooks do nothing. tests/platform/aux_cpus_test covers the derivation and live threads (CPU only).
noon-at-cgn
marked this pull request as ready for review
October 8, 2026 16:58
…n with the measured result
noon-at-cgn
force-pushed
the
pr/aux-cpus
branch
from
October 8, 2026 18:53
b79f197 to
1e80ce9
Compare
--aux-cpus: +10% decode on a layer split by keeping helper threads off the pool's cores (Linux, off by default)--aux-cpus: helper threads on spare CPUs, +10% decode on a layer split (Linux, opt-in)
Owner
|
Thanks. Ported into v0.1.42 with your commits. Reviewed: off by default, Linux only, threads only, no result changes. Measured on our boxes, 10 interleaved pairs, greedy ids identical in every pair. 2x R9700 (HIP, IQ3_S, layer split, other cards idle): decode +1.7% story, +1.8% code, +2.1% at 4K (9 of 10 pairs faster each), prompt -0.7 to -1.5% (1 to 2 of 10 pairs faster). P100 (one spare CPU): no difference (story 29.25 vs 29.10 tok/s, 3 of 10 pairs faster). RTX 3060 box (hybrid CPU): the plan found no spare CPU, so it does nothing there. So it stays opt-in; the prompt cost on a split is small but real. Your +10% on 2x RTX 3080 is not something we can reproduce without that pair. |
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.
Title
Issue: Related: none found (searched 2026-10-07 and 2026-10-08). Nearby: #1166 moves the host thread to its SMT sibling, #501 / #626 pin the host, #1436 gives replicas disjoint core masks; none places the engine's other threads.
Summary
Why it matters: the host thread is the one that answers every layer's doorbell during decode. Threads the engine starts later inherit its single CPU, and the CUDA driver's threads float over every CPU, the pool workers' included, so the host gets preempted in the middle of a window. Moving those threads to spare CPUs gave +10% decode on our box with no change to any result:
--aux-cpusoffauto--aux-cpus off|auto|LIST(orSTRATA_AUX_CPUS):autotakes the host core's SMT siblings, then CPUs of physical cores with no worker, never one that shares a core with a worker. It moves threads only, never a result. Default off: the hooks do nothing.What changed
include/strata/platform/aux_cpus.hpp(new, header only): spare-CPU derivation, list parser,pin_current_thread(), a sweep over/proc/self/taskfor the driver's threads, a way back to the old mask. Workers and host are never moved. Empty off Linux.src/program/generate.cpp: flag, variable, start-up plan, per-request switch; pins the adaptive tier's threads, stdin reader, watchdog.expert_source.cpp,ple_reader.cpp,direct_file.cpp,prefill.cpp: pin the look-ahead, PLE and file readers, prefill helpers, Stager copy threads (STRATA_AUX_STAGER=0leaves those).session.cpp,pool.cpp: mark host and workers.serve/server.py,serve/test_server.py:strata_tune {"aux_cpus": 1|0}is forwarded.tests/platform/aux_cpus_test.cpp+CMakeLists.txt;docs/DETAILS.md: section "Thread placement".Extra Notes
Measured on engine 0.1.40.3 (d5ea713) plus this change; the branch itself is cut from main fb58e0d.
Machine: 2x RTX 3080 20 GB (220 W), Xeon E5-2696 v4 (16 pool workers, host on CPU 2, helpers on CPU 24), 24 logical CPUs in the container, UD-Q4_K_XL, layer split 23,
--batch-mtp, adaptive tier on. Same binary in every arm, only the flag differs.auto. That preemption causes the loss is our inference; the counters do not prove it.Checked:
aux_cpus_test, pool and direct-file CPU tests,python -m unittest discover -s serve(594 tests), CUDA build. Not built or run: HIP, SYCL, Windows, macOS. The no-spare-CPU path was not tested.STRATA_ADAPT_JOB_CPUstill wins for the adaptive tier's job thread. Not covered: the Python launcher, the host thread.Context: this change is part of the setup measured in the community benchmark bench: community report, 2x RTX 3080 20 GB, UD-Q4_K_XL, 2 x 262K lanes in a 512K pool, 3.0K tok/s prompt, 86 tok/s decode #1640.