[tinker] Long-output results: pysimdjson decode, burst-survival settings, harness fidelity (131072 x 212k tokens) - #10
Closed
avigyabb wants to merge 3 commits into
Closed
Conversation
…djson With 262k-token results (2.8MB of JSON each) decoding the vLLM body was 72% of the API server's CPU even with results encoded to proto once: orjson.loads builds a Python object per token and logprob (22ms), then numpy converts the lists (16ms). CompletionDecoder parses the body with pysimdjson and extracts token_ids and token_logprobs as raw numeric buffers into int32/float32 arrays (~6ms, no per-token Python objects), falling back to orjson when pysimdjson is missing and per array when an array holds nulls. Zero-fill semantics for missing or null logprobs are unchanged. 512 concurrent 262k-token results, proto client: server CPU 16.6s -> 6.25s, 32 -> 89 results/s. 8192 such results with 5s engine queueing and SDK-style re-polls: 8192/8192, 0 failures, 79 results/s (~230MB/s), 15.9GB peak RSS at a 60s retrieved TTL. The load harness gains --retrieved-ttl for that knob. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Avi Basnet <avigyabb@stanford.edu>
…p-alive, sturdier router connects With 131072 outstanding samples and 212k-token results, each 2048-result completion burst kept the event loop busy for ~16s. uvicorn's 5s keep-alive then closed every idle SDK connection during the burst, every client reconnected at once, and the 2048-entry accept backlog overflowed, so the kernel refused connections (109k of 131072 requests failed at TCP level). - uvicorn.run: backlog=SKYRL_HTTP_CONNECTION_LIMIT (50k; the effective value is capped by net.core.somaxconn) and timeout_keep_alive=75s, so connections queue in the kernel and idle ones survive a burst. - Forwarding client: 60s connect timeout toward the router (a saturated router takes tens of seconds to accept; that is queueing, not failure) and Happy Eyeballs disabled. A burst of connect-timeout cancellations left ~800 uvloop "File descriptor N is used by transport" errors from aiohappyeyeballs' sock_connect, failing unrelated forwards. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Avi Basnet <avigyabb@stanford.edu>
…ake router, TTL knob - --max-outstanding emulates the SDK's sample_max_concurrent_requests (submit-through-result), --poll-timeout the SDK's 45s re-poll, and asample / retrieve_future connection or timeout failures retry up to 16 times with exponential backoff like the SDK instead of a global budget. - --router-workers N forks fake-router processes sharing the port (SO_REUSEPORT) with shared counters, so 2.3MB results no longer saturate a single router process; workers are terminated on SIGTERM. - --retrieved-ttl passthrough; EngineConfig fields are passed only if the checked-out commit has them, so the same harness baselines older commits. - Credit Chuck Tang's repro gist, which the harness structure follows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Avi Basnet <avigyabb@stanford.edu>
Owner
Author
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.
Stacked on #9. With results the size of Chuck's repro (262k tokens, 2.8 MB of JSON per result), decoding the vLLM body was 72% of the API server's CPU even after #9's proto-once path:
orjson.loadsbuilds one Python object per token and logprob (22 ms), then numpy converts the lists (16 ms). This PR decodes the two numeric arrays straight into int32/float32 buffers.Change
skyrl/tinker/extra/completion_decode.py:CompletionDecoderparses/v1/completionsbodies with pysimdjson and pullstoken_ids/token_logprobsout as raw numeric buffers (Array.as_buffer), so a 262k-token result decodes in ~6 ms with no per-token Python objects. Falls back to orjson when pysimdjson is not installed, and per array when an array is not purely numeric (a null logprob). Zero-fill semantics for missing/null logprobs are unchanged from the forwarding client.SkyRLTrainInferenceForwardingClient._forwarduses it; the decoded arrays feedserialize_sample_outputdirectly (numpy in, no copy).pysimdjsonadded to thetinkerextra (uv.lock+46 lines).--retrieved-ttlpassthrough so long-output runs can bound retention memory.Decode alternatives measured on one 262k-token body: current 38.6 ms;
np.fromstringon extracted substrings 28.9 ms; pysimdjson 6.2 ms.Results (proto client, 262k-token results, 512 outbound cap)
Memory at this size is retention: completion rate x 2.1 MB proto x
external_future_retrieved_ttl_sec. The default 300 s would hold ~50 GB at 80 results/s, so operators running 262k-token rollouts should set the TTL to 60-120 s (the SDK re-polls a lost response within 45 s + 30 s backoff).Tests:
tests/tinker/test_completion_decode.pycovers both backends, nulls, empty arrays, prompt-logprob passthrough, non-JSON bodies, parser reuse, and 200k-element parity between the fast and fallback paths.Two more commits: surviving completion bursts, and harness fidelity
f3a58f1bserver: with 131072 outstanding samples and 212k-token results, each 2048-result completion burst kept the loop busy ~16 s; uvicorn's 5 s keep-alive then closed every idle client connection, all clients reconnected at once and the 2048-entry accept backlog overflowed (109k of 131072 requests refused at TCP level).uvicorn.runnow usesbacklog=SKYRL_HTTP_CONNECTION_LIMIT(as on Chuck's branch; effective value capped bynet.core.somaxconn, raise it to match) andtimeout_keep_alive=75. The forwarding connector gets a 60 s connect timeout and Happy Eyeballs off: a wave of 10 s connect timeouts toward a saturated router produced ~800 uvloopFile descriptor N is used by transporterrors out of aiohappyeyeballs' cancelledsock_connect, failing unrelated forwards.e295b282harness:--max-outstandingemulates the SDK'ssample_max_concurrent_requests; asample/retrieve failures retry 16x with backoff like the SDK;--router-workers Nruns the fake router across processes (one aiohttp process saturates at ~250 MB/s of 2.3 MB bodies);--retrieved-ttl.131072 requests x 212k-token results (proto client, 2048 forwarding cap, 5 s engine queueing, 16384 in flight, 60 s retrieved TTL)
asampleresponses took >45 s (the SDK retries these; the harness at the time did not)Caveat on the last row: four router workers from a preceding smoke run were still bound to the port (fixed in
e295b282), so part of the traffic saw 2 s instead of 5 s generation; the API-server measurements are unaffected.Where the API server stands at this payload size: ~9 ms of CPU per result (decode + proto) plus ~2 ms of HTTP, so one process delivers 110-120 results/s of 212k-token output. Retention memory is completion rate x 1.7 MB x retrieved TTL. In production the vLLM API server serializing 2.3 MB of JSON per result is likely to saturate first.
Before / after vs upstream
main(345ce86), same harness and SDK-shaped clientmainAnd on pre-NovaSky-AI#2097
main(59d4daed, where Chuck saw 32x64 fail) with 262k results: 512 barrier -> 511/512 in 1441 s withQueuePooltimeouts; 2048 with queueing -> 18/2048 in 39 min, 4218QueuePoolerrors, the client saw "QueuePool limit of size 5 overflow 10 reached". The same 512 case here: 5.8 s.Tests: tinker + utils CPU suite, 117 passed.
🤖 Generated with Claude Code