Repository navigation
[Engine] serve: opt-in STRATA_SLOT_EXPIRE_S keeps a long conversation's batch slot from a short side request (next turn 12.8 -> 0.3 s) - #2589
Conversation
|
|
5815002 to
ee66067
Compare
|
Thanks for the pull request. We're resetting the tracker for 0.2.0-beta. There are more than 1,300 open issues and pull requests, too many for us to review properly, so we're closing everything that isn't being worked on right now. Closed here does not mean rejected. To reopen it takes about two minutes:
The rules are in CONTRIBUTING.md. |
…onversation cheapest to lose With no empty slot, pick_slot takes the least recently used one. A short side request (a title, a one-line question) then takes the slot of a long conversation that is still going, when that conversation's last turn ended before the other slot's, and its next turn reads the whole history again. New, off unless set: STRATA_SLOT_EXPIRE_S (seconds, in the server's environment). Set, the order is an empty slot, then an expired one (idle that long or longer: its client has likely moved on), then the one holding the shortest history, the least recently used among equals. Unset or 0, every held slot counts as expired and the least recently used goes, as before. A request still goes to the slot holding its own prefix first, and the pipelined groups' order (Niko1221#793) is unchanged. serve/test_slot_alloc: one test per rule, the measured case below, random slot tables checked against the rule and (off) against the old order, and the variable's values. docs/BATCHING.md says the order and the variable. Measured on one RTX 5090 (engine from main 296b59d, --batch 3, no conversation cache, a third stream decoding all along, the rule at 600 s): with a 40,248-token and a 1,777-token conversation held, a one-line side request took the long one's slot on main, and its next turn read all 40,273 tokens again (12.72 and 12.70 s to the first token); with the rule it takes the short one's slot and the long one's next turn reuses 40,251 tokens (0.41 and 0.34 s). Replayed on a 21-hour timeline of my own agent traffic (9,771 requests, 4,503 conversations; ids, times and token counts only) through 4 / 6 / 8 slots, STRATA_SLOT_EXPIRE_S=600 reads 17.8 / 20.6 / 21.1% fewer prompt tokens than the least-recently-used order; the best possible order, computed knowing the whole timeline, reads 18.5 / 22.6 / 22.4% fewer. Thinned to 5-50% of its conversations for smaller setups, 2 to 4 slots read 0.3-22.5% less on average; 1 slot has nothing to choose. Where it loses: a long conversation whose client has gone keeps its slot until it expires while busy short chats share the other slots.
The engine reads it from its own environment (child_env: the server's, plus the config's "env" block), so
"env": {"STRATA_SLOT_EXPIRE_S": "600"} in strata-<model>.json works like the other STRATA_* switches; the server's
environment still works, and the config wins when both are set. Found by qiuzan2001 in review.
|
/reopen |
|
Reopened, thank you. A maintainer will pick it up from here. If it conflicts with main, rebase it first; that speeds up review. |
ee66067 to
ca7d91a
Compare
Problem and result
I run coding agents on Strata with
--batch. Next to their long conversations, clients send short side requests (a title, a summary, a one-line check). When every free slot holds a conversation,pick_slottakes the least recently used one, so a side request can take the slot of a 40K-token conversation that is still going, and that conversation's next turn reads its whole history again: 12.8 s to the first token on 0.2.0-beta (one RTX 5090, below). With the new opt-inSTRATA_SLOT_EXPIRE_S=600the side request takes an expired slot or the one holding the shortest history, and the long conversation's next turn takes 0.34 s. Unset or 0, nothing changes.Related discussion
#1906 and #1930 (agent sessions reading whole prompts again), #1920, #1938. Next to #2527 / #2528 (andyhrc): their placement policy covers pipelined groups; this is the small counterpart for
--batchwithout pipelined groups (one GPU, or--batch-groups 1). qiuzan2001 noted here that the config's"env"block was ignored: the value is now read from the engine's environment (the server's plus the config's"env"). Rebased onto 0.2.0-beta after the tracker reset (one textual conflict: two functions added at the same place).Approach
STRATA_SLOT_EXPIRE_S(seconds; off unless set). Set, a new conversation that finds no empty slot takes an expired one first (idle that long or longer: its client has likely moved on), the least recently used among them; else the one holding the shortest history. Unset or 0: the least recently used one, as before (a test checks it picks main's slot on 3,000 random slot tables). A request still goes to the slot holding its own prefix first, and pipelined groups return before this rule. Changes:pick_slot's lastmin()key,slot_expire_s(env),serve/test_slot_alloc.py, and the sentence on slot choice indocs/BATCHING.md.Validation
Passed:
python -m unittest serve.test_slot_alloc25 OK; the whole serve suite (python -m unittest discover -s serve -t . -p "test_*.py") 899 OK, 21 skipped, against 881 OK, 21 skipped on main.--batch 3, no pipelined groups, no conversation cache, a fresh engine per arm,STRATA_SLOT_EXPIRE_S=600in the config's"env"block (main ignores it). A third stream decodes all along, so every request goes through a slot. Conversation A (a 40,250-token prompt) and B (about 1,770 tokens) finish and stay in their slots; then a one-line side request; then A's and B's next turns:STRATA_SLOT_EXPIRE_S=600Failed: none observed.
Not run: a split with
--batch-groups 1on GPUs; the RAM conversation cache on (replay only: smaller gain); AMD, Linux, Mac (Python-only change, nothing to build).Limits
Opt-in, because it changes whose conversation loses its slot and is measured on one user's traffic. A long conversation whose client has gone keeps its slot until it expires; a client whose prompts never match its slot leaves histories that stay until they expire.