mstar.engine.cuda_graph_runner#
Functions
|
AND each flag across every rank of the joint group. |
|
A forward's autocast scope; |
|
Capture |
|
Capture |
|
Stand-in request info for padding rows, which have no real request. |
|
|
|
The name a padding-row handle was minted from. |
|
MSTAR_REQUIRE_CUDA_GRAPHS=1 turns a dropped bucket into a startup failure, for deployments that would rather not come up than serve the eager path at 10-20x the latency. |
Classes
|
The captured slots for one (walk, cg_key_info, bs, num_tokens). |
|
|
|
One captured graph + the buffers its replay reads and writes. |
|
The padding rows captured replays pad onto. |
|
Captures one inner callable of a submodule's forward as a CUDA graph. |
|
One captured region, for one (bs, total_tokens) bucket. |
|
|
|
Dict-like view over a captured region's output buffers. |
- class mstar.engine.cuda_graph_runner.CudaGraphBucket(config, config_idx, slots=<factory>)[source]#
Bases:
objectThe captured slots for one (walk, cg_key_info, bs, num_tokens).
- Parameters:
config (CudaGraphConfig)
config_idx (int)
slots (list[CudaGraphSlot])
- config: CudaGraphConfig#
- slots: list[CudaGraphSlot]#
- class mstar.engine.cuda_graph_runner.CudaGraphRunner(submodule_name, submodule, resources, step_runner, device, autocast_dtype, joint_comm_group, num_slots, enable_nvtx=False)[source]#
Bases:
object- Parameters:
submodule_name (str)
submodule (NodeSubmodule)
step_runner (StepRunner)
device (device)
autocast_dtype (dtype | None)
joint_comm_group (JointGroups)
num_slots (int)
enable_nvtx (bool)
- CAPTURE_BATCH_SIZES = [1, 2, 4, 8, 16, 32, 64]#
- NUM_WARMUP = 2#
- property any_graphs#
- declare_inputs_for(lease)[source]#
Template rows for a declare-only call, cached per bucket.
A bucket’s shape is fixed, so these rows are constant; building them fresh cost ~130us of clones per pre-plan at bs=16, plus that many D2D copies. Only for callers that hand the rows to
declare_stepand nothing else — the list is shared, so a caller that mutates it or passes it topreprocesscorrupts every later step on this bucket.- Parameters:
lease (SlotLease)
- Return type:
- lease_slot(graph_walk, bs, slot, num_tokens=None, cg_key_info=None)[source]#
Pick the bucket and double-buffer slot an upcoming step replays on.
num_tokens=Nonemeans the batch’s inputs aren’t built yet, so the bucket comes from the batched-capture search instead.slot=Noneadvances the bucket’s counter so the next lease lands on the other slot; a caller that already reserved one (pre-plan) passes it back so both submissions target the same slot.
- max_batch_size_for(graph_walk)[source]#
Largest batch this walk was captured for, or None for no cap.
A config that opts out of capping captures only an acceleration subset of batch sizes — larger batches run eager rather than being split — so it does not constrain the batch. With every config for the walk opting out, nothing here caps it.
- pad_inputs(lease, inputs)[source]#
Real inputs plus the rows that bring the batch to capture batch size. Set the length to zero so no new pages are allocated for dummy requests.
- Parameters:
lease (SlotLease)
inputs (list[NodeInputs])
- Return type:
- plan_stream()[source]#
Dedicated stream for pre-planning.
Pre-plan must not submit onto the default stream: whether its memcpys land before or after the GPU thread records the previous batch’s completion event is timing-dependent, and landing after delays that event past pre-plan’s own kernels. Its own stream keeps the two independent; a plan-done event gates the replay that reads the buffers.
- Return type:
Stream | None
- prepare_for_capture()[source]#
Claim the static buffers this runner’s captures will read.
Split from the capture so every runner claims first: nodes share resources, and a build driven by a later node would move buffers an earlier node’s graphs already baked in.
- Return type:
- release(lease, real_bs)[source]#
Return the padding rows to their at-rest state after a step.
Padding rows (is_padding_row) run against SINK_PAGE and the recurrent pool’s sink slot, so nothing stays resident: padding must never compete with real requests for storage.
- run_forward(lease, preprocessed, plan_done_event=None, launch_started_event=None)[source]#
Stage this step’s inputs into the slot and replay it.
plan_done_eventis the pre-plan’s event: its plan wrote this slot’s buffers on another stream, so the replay has to wait for those writes before it reads them.launch_started_eventreleases the submitting thread. It is set HERE rather than before the forward: staging copies each preprocessed tensor into its static buffer and holds the GIL while doing so, so releasing earlier hands the main thread a GIL the GPU thread still needs. Only graph.replay() below drops it in C++.
- select_batched_bucket(graph_walk, bs, cg_key_info=None)[source]#
A bucket for a batch whose inputs aren’t built yet.
Only a batched capture can answer this: its token count is a property of the config (
bsrows of a fixed per-request length), so the bucket follows from the batch size alone. A packed capture’s token count is a property of the requests, so it has to wait forprepare_inputsand go throughselect_bucket.This is what lets pre-plan run before inputs exist. Lifting the restriction means making
prepare_inputssafe to run ahead of the forward — seeEngine.pre_plan_for_batch.
- select_bucket(graph_walk, bs, num_tokens, cg_key_info=None)[source]#
Tightest captured bucket that fits this batch, or None for eager.
A walk may have several captures (e.g. one per image resolution, each a fixed shape with its own token count), so every matching bucket is considered rather than the first config declared.
num_tokenscounts the batch’s input tokens; a bucket is keyed by the tokens its plan carries, so the request is scaled by that config’s multiplier.
- step_ids(lease, request_ids)[source]#
The padded addressing for one step: real ids first, then the slot’s padding ids. Plans, commits, and advances address real request state by its own id; only the padding rows run against the slot’s own state.
- class mstar.engine.cuda_graph_runner.CudaGraphSlot(graph, static_inputs, static_input_keys, static_outputs, dummy_rids, dummy_metadata, config_idx)[source]#
Bases:
objectOne captured graph + the buffers its replay reads and writes.
Double-buffer: a bucket holds one of these per slot. Replay alternates between slots so plan(N+1) on the inactive slot’s resources can run concurrently with replay(N) on the active slot — so a node with nothing to pre-plan keeps a single slot.
- Parameters:
- dummy_metadata: dict[int, CurrentForwardPassInfo]#
- static_outputs: BatchedModelOutput#
- class mstar.engine.cuda_graph_runner.DummyRowPool(prefix, step_runner, resources)[source]#
Bases:
objectThe padding rows captured replays pad onto.
Ingested once and kept for the runner’s lifetime: a re-ingest would hand the resources a fresh stream and orphan the pages the last capture left resident, and keeping them resident is what lets a step’s padding tail allocate nothing.
- Parameters:
prefix (str)
step_runner (StepRunner)
- handles(key, bs)[source]#
bspadding-row handles forkey. Negative and stable per (prefix, key, index), so they never collide with a real request’s handle nor with another pool’s rows.
- class mstar.engine.cuda_graph_runner.PiecewiseCudaGraphRunner(label, config, resources, step_runner, device, autocast_dtype, num_slots, joint_comm_group=None, node_name=None, memory_pool=None)[source]#
Bases:
objectCaptures one inner callable of a submodule’s forward as a CUDA graph.
Where
CudaGraphRunnerreplays a wholeforward_batchedunder engine control, this captures a SUB-REGION — a transformer block loop, say — while the surrounding preamble stays eager and the submodule invokesrunitself. The config supplies the callable, its static buffers, and the region’s own step declaration; the runner drives that step through the same admit → plan → commit cycle the engine uses, so the region’s attention plan lands in the resources’ per-(bucket, slot, label) buffers and the captured graph reads them at fixed addresses.- Parameters:
label (str)
config (PiecewiseCudaGraphConfig)
step_runner (StepRunner)
device (device)
autocast_dtype (dtype | None)
num_slots (int)
joint_comm_group (JointGroups | None)
node_name (str | None)
- CAPTURE_BATCH_SIZES = [1, 2, 4, 8, 16, 32, 64]#
- NUM_WARMUP = 2#
- lease_slot(batch_size, total_tokens=None)[source]#
The bucket this batch would replay on, asked before the forward.
Same question
runsettles per call, answered early so the outerdeclare_stepcan plan the region’s resources against it.
- prepare_for_capture()[source]#
Claim this region’s static buffers; see
CudaGraphRunner.- Return type:
- run(static_inputs, request_ids=None, seq_lens=None, real_bs=None)[source]#
Replay the captured region for these real inputs.
Copies each real input into the runner-owned buffer of the same name, declares and plans the region’s step over the padded batch, replays, then commits and returns the padded buffers behind a real-length view.
Only the static buffers carry data into a replay: the region’s Python ran once, at capture, so whatever it read off
PiecewiseCallInputsis baked into the graph.
- class mstar.engine.cuda_graph_runner.PiecewiseGraphData(graph, static_inputs, static_outputs, dummy_rids, shape, bucket)[source]#
Bases:
objectOne captured region, for one (bs, total_tokens) bucket.
- Parameters:
- shape: PiecewiseCaptureShape#
- class mstar.engine.cuda_graph_runner.PiecewiseGraphKey(bs, seq_len, slot)[source]#
Bases:
NamedTuple
- class mstar.engine.cuda_graph_runner.PiecewiseOutput(outputs, real_len)[source]#
Bases:
objectDict-like view over a captured region’s output buffers.
The runner replays into persistent buffers sized for the padded bucket; only the leading
real_lenrows are meaningful. Indexing andgetreturn an owned CLONE of that leading slice — safe to keep past the next replay.get_viewreturns the same slice WITHOUT copying.- get_view(key, default=None)[source]#
The leading
real_lenslice WITHOUT copying.The result aliases the runner-owned static output buffer and is OVERWRITTEN by the next
runof this runner. Nothing else writes there: the buffers live outside the graph memory pool, so replays of other regions sharing the node’s pool leave them alone. Read it within the same step; usegetwhen you need something that outlives it.- Parameters:
key (str)
- mstar.engine.cuda_graph_runner.agree_across_ranks(comm_group, flags, device)[source]#
AND each flag across every rank of the joint group.
Capture failure is per-rank: a rank that keeps a graph another rank dropped replays it while the other runs eager, which hangs as soon as the captured region holds a collective. Callers pass an order derived from the configs, which are identical on every rank.
- mstar.engine.cuda_graph_runner.autocast_scope(dtype, device_type='cuda')[source]#
A forward’s autocast scope;
None(disable_autocast) runs the submodule in its own dtype and shuts out any ambient autocast.
- mstar.engine.cuda_graph_runner.capture_into_graph(run, pool, device, autocast_dtype)[source]#
Capture
runinto a new CUDA graph allocated frompool.Returns the graph and what
runreturned. A capture that fails part way leaves two things behind that would sink every later capture on the same pool: the caching allocator keeps routing this thread’s allocations into the pool (the next capture_begin fails with “already recording to mempool_id”), and torch.cuda.graph never switches the thread back off its capture stream. Undo both before re-raising, so the buckets after a failed one still capture and the thread keeps running on the stream it came in on.A torch.compile’d forward that would recompile inside the capture is failed up front with the guard that broke: dynamo saves the CUDA RNG state before it compiles, which CUDA refuses during capture, so such a capture cannot succeed and would otherwise die with an unrelated error.
- mstar.engine.cuda_graph_runner.capture_with_static_outputs(run, warm_outputs, pool, device, autocast_dtype)[source]#
Capture
runso its outputs land in buffers allocated OUTSIDEpool.warm_outputsis whatrunreturned on a warm-up call ({name: Tensor}); it fixes the shapes and dtypes. The captured graph ends with a device-to-device copy of each output into a buffer allocated here, before the capture, from the ordinary allocator.Why not hand out the graph’s own output tensors: blocks a graph frees while it is being captured go back to the pool and are handed to the graphs captured after it, so an output that lived in the pool could sit exactly where an earlier-captured graph keeps its scratch and be overwritten by that graph’s next replay. That is harmless while a pool belongs to one region whose graphs replay one at a time and whose outputs are consumed before the next replay, but not once a node’s regions share one pool and replay in data-dependent order. Buffers outside the pool stay valid until this graph’s own next replay, whatever else the node runs in between.
- mstar.engine.cuda_graph_runner.dummy_metadata(rids, graph_walk)[source]#
Stand-in request info for padding rows, which have no real request.