1. The per-microgroup pipeline
miles/rollout/sglang_diffusion_rollout.py runs, per microgroup:
The rollout pipeline decomposes into generate / deserialize / reward components. Because every microgroup is its
own asyncio task, the engine starts the next request while previous responses are still being unpacked and scored.
Nothing waits for the full batch.
2. Deserialization: msgpack + parser-actor pool
Trajectory tensors are large — for video models the response for one microgroup can be gigabytes, and encoding tensors into base64 further increases the size. In a naive implementation, tensors were base64-encoded inside a JSON body and parsed on the main asyncio event loop, one sample at a time. The current path:- msgpack raw-bytes transport. The engine responds with
application/msgpack;post(..., raw=True)returns the body untouched, and tensors decode directly from safetensors raw bytes — no base64. - Unpacking runs inside Ray actors, not the event loop.
RolloutImageResponseParserActor.apply_raw(samples, raw)doesmsgpack.unpackb+ tensor decode in a separate process (miles/utils/diffusion_rollout_response.py), so the rollout event loop never blocks on a multi-GB unpack. - One call per microgroup. A whole microgroup is parsed in a single
apply_rawcall — fewer Ray RPCs, one unpack per response. - A pool, round-robin dispatched.
--rollout-parser-num-workers Nspins up N parser actors so multiple microgroups deserialize in parallel.
3. Reward: scored as soon as a microgroup lands
generate_and_rm_microgroup calls batched_async_rm immediately after parsing, per microgroup — not once at the end of
the rollout:
4. Diagnosing the pipeline
The miles dashboard visualizes the lifetime of every request — the generate / deserialize / reward spans of each microgroup on one timeline, so overlap (or the lack of it) is visible directly. Launch with--use-miles-dashboard
(telemetry lands under --miles-dashboard-workspace), then render:
perf/* metrics alone:

