CUDAGraph#
- class torch.cuda.CUDAGraph(keep_graph=False)[source]#
Wrapper around a CUDA graph.
- Parameters:
keep_graph (bool, optional) – If
keep_graph=False, the cudaGraphExec_t will be instantiated on GPU at the end ofcapture_endand the underlying cudaGraph_t will be destroyed. Users who want to query or otherwise modify the underlying cudaGraph_t before instantiation can setkeep_graph=Trueand access it viaraw_cuda_graphaftercapture_end. Note that the cudaGraphExec_t will not be instantiated at the end ofcapture_endin this case. Instead, it will be instantiated via an explicit call toinstantiateor automatically on the first call toreplayifinstantiatewas not already called. Callinginstantiatemanually beforereplayis recommended to prevent increased latency on the first call toreplay. It is allowed to modify the raw cudaGraph_t after first callinginstantiate, but the user must callinstantiateagain manually to make sure the instantiated graph has these changes. Pytorch has no means of tracking these changes.- Return type:
Self
Warning
This API is in beta and may change in future releases.
- capture_begin(pool=None, capture_error_mode='global', check_input_liveness=False)[source]#
Begin capturing CUDA work on the current stream.
Typically, you shouldn’t call
capture_beginyourself. Usegraphormake_graphed_callables(), which callcapture_begininternally.- Parameters:
pool (optional) – Token (returned by
graph_pool_handle()orother_Graph_instance.pool()) orMemPoolthat hints this graph may share memory with the indicated pool. See Graph memory management.capture_error_mode (str, optional) – specifies the cudaStreamCaptureMode for the graph capture stream. Can be “global”, “thread_local” or “relaxed”. During cuda graph capture, some actions, such as cudaMalloc, may be unsafe. “global” will error on actions in other threads, “thread_local” will only error for actions in the current thread, and “relaxed” will not error on these actions. Do NOT change this setting unless you’re familiar with cudaStreamCaptureMode
check_input_liveness (bool, optional) –
If
True, tracks external tensor inputs during graph capture and raises an error if any are deallocated before replay. This helps debug “use after free” errors where input tensors are garbage collected between capture and replay. Default:False.Note
Custom CUDA kernels added outside PyTorch (e.g., via cuLaunchKernel or DLPack) are not tracked by this mechanism.
- capture_end()[source]#
End CUDA graph capture on the current stream.
After
capture_end,replaymay be called on this instance.Typically, you shouldn’t call
capture_endyourself. Usegraphormake_graphed_callables(), which callcapture_endinternally.
- capture_end_post()[source]#
Finalize a capture started by
capture_end_pre(): destroy the template whenkeep_graph=False(the graph must already be instantiated;capture_end()and the context manager do so).
- capture_end_pre()[source]#
End capture but do not finalize: leaves the captured
cudaGraph_tlive (for bothkeep_graphmodes) so it can be inspected beforecapture_end_post()instantiates and/or destroys it.
- debug_dump(debug_path, *, verbose=True)[source]#
Dump the captured graph to
debug_pathin Graphviz DOT format.The graph’s template must be live:
keep_graph=True(orenable_debug_mode()), or called from a capture-end hook. Requires thecuda.bindingspackage.- Parameters:
debug_path (required) – Path to dump the graph to.
verbose (bool) – If
True(default), use the most verbose DOT output.
- enable_debug_mode()[source]#
Retain the captured graph (equivalent to
keep_graph=True) so it can be inspected, e.g. viadebug_dump(). Kept for backward compatibility.
- get_graph_data()[source]#
Return a dictionary describing the graph’s topology and node metadata.
keep_graphmust be True. The graph must have been instantiated (viainstantiate()) before calling this method. Requires thecuda.bindingspackage.Returns a dictionary with structure:
{ "exec_graph_id": int, "nodes": [ { "index": int, "node_type": str, "tools_id": int, "graph_id": int, "node_id": int, "kernel_name": str or None, "event_ptr": int, "host_fn_addr": int, "host_fn_name": str or None, "dependencies": [int, ...], "dependents": [int, ...], }, ..., ], }
event_ptris thecudaEvent_thandle (as an int) an event-record or event-wait node records / waits on – these nodes produce no timed CUPTI record, so matching a wait to the record that signals it is the only way to reason about the cross-stream sync it encodes. It is0for other node types.host_fn_addr/host_fn_nameare populated for host nodes (a CPU callback run as a graph node): the callback address and a best-effort demangled symbol name for it (Nonewhen it resolves to no exported symbol). They are0/Nonefor other node types.Each node’s
graph_idis remapped to the exec graph id so thattools_idvalues match those reported by CUPTI-based profilers.dependenciesanddependentsare lists of node indices within thenodeslist.This structure is useful for inspecting a profiler trace and establishing whether a particular dependency observed in the profile is a true dependency (encoded in the graph) or a fake dependency caused by mapping of independent streams to the same hardware channel.
Child-graph and conditional nodes are reported as nodes in their own right, but this walk does not descend into their bodies (those are separate
cudaGraph_tobjects), so the work inside them is absent fromnodesand a warning is issued. The ids that are reported stay valid: the exec graph preserves top-level node ids.- Return type:
- instantiate()[source]#
Instantiate the CUDA graph. Will be called by
capture_endifkeep_graph=False, or byreplayifkeep_graph=Trueandinstantiatehas not already been explicitly called. Does not destroy the cudaGraph_t returned byraw_cuda_graph.
- pool()[source]#
Return an opaque token representing the id of this graph’s memory pool.
This id can optionally be passed to another graph’s
capture_begin, which hints the other graph may share the same memory pool.- Return type:
_POOL_HANDLE
- pools()[source]#
Return opaque tokens for all memory pools retained by this graph.
- Return type:
list[_POOL_HANDLE]
- raw_cuda_graph()[source]#
Returns the underlying cudaGraph_t. The template must be live: this requires
keep_graph=True(it persists aftercapture_end), or access from within a capture-end hook (before the template is destroyed forkeep_graph=False).See the following for APIs for how to manipulate this object: Graph Management and cuda-python Graph Management bindings
- Return type:
- raw_cuda_graph_exec()[source]#
Returns the underlying cudaGraphExec_t.
instantiatemust have been called ifkeep_graphis True, orcapture_endmust have been called ifkeep_graphis False. If you callinstantiate()afterraw_cuda_graph_exec(), the previously returned cudaGraphExec_t will be destroyed. It is your responsibility not to use this object after destruction.See the following for APIs for how to manipulate this object: Graph Execution and cuda-python Graph Execution bindings
- Return type:
- register_capture_end_hook(hook)[source]#
Register
hook(graph)to run when capture ends, after capture completes but before the graph is finalized. The capturedcudaGraph_tis live (viaraw_cuda_graph()) for bothkeep_graphmodes. Hooks fire in registration order. Returns a handle whoseremove()deregisters the hook.- Return type:
RemovableHandle
- register_capture_start_hook(hook)[source]#
Register
hook(graph)to run when capture begins on this graph, right after capture is under way on the current stream. Hooks fire in registration order. Returns a handle whoseremove()deregisters the hook.Warning
The hook runs inside the capture: any CUDA work it issues is captured into the graph, and under the default
"global"capture error mode an unsafe call raises. Seetorch.cuda.graphs.register_graph_capture_start_hook().- Return type:
RemovableHandle
- register_destroy_callback(cb, *, synchronize_before_release=False)[source]#
Register
cb()to run when this graph is destroyed (finalized) or explicitlyreset(), just before its CUDA resources are freed. Callbacks fire once per capture cycle, in registration order; exceptions are swallowed so one failure does not abort the rest.cbmust NOT reference this graph: the finalizer that fires it is held by a global registry, so a callback reachable to the graph keeps the graph alive until interpreter exit (it is never collected, hence never fired). Returns a handle whoseremove()deregisters the callback.Teardown does not synchronize CUDA, and
cudaGraphExecDestroyfrees an in-flight graph only asynchronously, so a callback that frees device memory the graph reads/writes is a use-after-free if a replay is still in flight. Passsynchronize_before_release=Trueto synchronize every stream this graph was replayed on before firing. Otherwise callbacks must not free anything the graph references.- Return type:
RemovableHandle
- register_post_instantiate_hook(hook)[source]#
Register
hook(graph)to run after each instantiation (including re-instantiation, which produces a fresh exec graph). The instantiated graph is available viaraw_cuda_graph_exec(). Hooks fire in registration order. Returns a handle whoseremove()deregisters the hook.- Return type:
RemovableHandle
- register_replay_end_hook(hook)[source]#
Register
hook(graph)to run at the end of everyreplay(), just after the graph is launched. The launch is asynchronous, so the hook runs once the replay is enqueued, not once the GPU work completes. Hooks fire in registration order. Returns a handle whoseremove()deregisters the hook. See the hot-path note onregister_replay_start_hook().End hooks fire even if the launch raises – so a start hook is always balanced by an end – and the launch error then propagates. (Start hooks that raise abort the replay before launch, and no end hook fires.)
- Return type:
RemovableHandle
- register_replay_start_hook(hook)[source]#
Register
hook(graph)to run at the start of everyreplay(), just before the graph is launched (after any on-demand instantiation, soraw_cuda_graph_exec()is valid). Hooks fire in registration order. Returns a handle whoseremove()deregisters the hook.Note
Replay is the hot path and a registered hook runs on every replay – keep it cheap. With no hook registered the cost is a single dict emptiness check.
- Return type:
RemovableHandle
- retain_object(obj, *, synchronize_before_release=False)[source]#
Keep
objalive for this graph’s current capture cycle and release it when the graph is destroyed (finalized) or explicitlyreset(). No callback runs; normal refcounting dropsobjwhen the retained reference is released. Returns a handle whoseremove()drops the retained reference early. As withregister_destroy_callback(),objmust NOT reference this graph, or the graph is kept alive until interpreter exit andobjis never released.synchronize_before_releasehas the same meaning and caveats as inregister_destroy_callback(): set it if releasingobjfrees device memory the graph reads/writes (e.g.objis the last reference to a tensor the graph uses) and replays may still be in flight.- Return type:
RemovableHandle