Skip to content

cuda.core: add atomic update methods for executable graph nodes #2354

Description

@Andy-Jost

Background

Part of #1330. Depends on #2350, #2352, and #2353.

CUDA supports individual executable-node updates but provides no parameter getters for CUgraphExec. A source CUgraph may also be modified independently after instantiation, so it cannot reliably represent the executable graph's current parameters.

Add exec-bound node update views that require complete replacement parameters and retain new owners through the append-only accumulator from #2353.

This feature requires CUDA 12.2+ and uses the generic cuGraphExecNodeSetParams API. Supporting CUDA 12.0/12.1 through the older typed setters is intentionally out of scope.

Proposed API

Index an executable graph with a node from a graph definition associated with it:

graph[kernel_node].update(
    config=new_config,
    kernel=new_kernel,
    args=new_args,
)

graph[memcpy_node].update(
    dst=new_dst,
    src=new_src,
    size=new_size,
)

graph[memset_node].update(
    dst=new_dst,
    value=new_value,
    width=new_width,
    height=new_height,
    pitch=new_pitch,
)

graph[host_node].update(new_callback, user_data=new_user_data)
graph[child_node].update(new_child_graph)
graph[event_node].update(new_event)

exec_kernel = graph[kernel_node]
exec_kernel.disable()
assert not exec_kernel.is_enabled
exec_kernel.enable()

Unlike definition-level updates, executable-node updates require a complete parameter set. cuda.core does not cache or infer omitted values because it cannot query current executable-node state reliably.

The returned exec-bound view does not expose parameter properties as current executable state. It is a lightweight pairing of one executable and one definition node for an immediate operation.

Node identity and lifetime

GraphExecBox does not retain or mirror the source graph hierarchy. CUDA remains authoritative for whether a CUgraphNode identifies a node in a given CUgraphExec.

  • A GraphNodeHandle already retains the definition graph hierarchy that owns it.
  • An ephemeral exec-bound view retains its GraphExecHandle and GraphNodeHandle, keeping both CUDA handles alive for the operation.
  • If the user discards the source graph wrapper but retains a node or view, that node's source hierarchy remains alive.
  • If the user discards the source graph, all of its nodes, and all views, the source CUgraph is reclaimed while the executable remains launchable and updatable through a future whole-graph update.
  • cuda.core validates the local node state and supported node type. The CUDA executable-node API validates whether the node belongs to the executable, including after whole-graph updates.
  • Removing a node invalidates its managed handle before the driver call.
  • Modifying source-node parameters does not affect the exec and is never used to infer executable state.

No source identity, source generation, or executable parameter cache is maintained in cuda.core.

Update transaction

Each update() call constructs one fully initialized, zero-padded CUgraphNodeParams from its complete arguments and invokes cuGraphExecNodeSetParams exactly once.

For owner-changing updates:

  1. Resolve all owners and construct the complete parameters.
  2. Record the current exec accumulator size.
  3. Append every required owner before calling CUDA.
  4. Call cuGraphExecNodeSetParams.
  5. On success, keep the appended owners.
  6. On failure, restore the accumulator to its previous size.

Successful updates never remove prior owners individually. Superseded owners remain until a successful Graph.update() replaces the accumulator or the executable graph is destroyed.

Raw addresses remain caller-owned and do not add accumulator entries.

Supported nodes

Implement exec-bound update() for:

  • KernelNode
  • MemcpyNode
  • MemsetNode
  • HostCallbackNode
  • ChildGraphNode
  • EventRecordNode
  • EventWaitNode

Empty nodes have no mutable parameters. CUDA does not support executable parameter updates for allocation, free, or conditional nodes.

Enable and disable

Exec-bound views for KernelNode, MemcpyNode, and MemsetNode additionally expose:

view.is_enabled
view.enable()
view.disable()

is_enabled queries cuGraphNodeGetEnabled and is not cached. enable() and disable() call cuGraphNodeSetEnabled and propagate driver failures through the normal CUDAError path. Both methods are idempotent. Parameter updates and whole-graph updates do not alter the executable node enable state.

Driver APIs

Use cuGraphExecNodeSetParams for every supported parameter update. Use cuGraphNodeGetEnabled and cuGraphNodeSetEnabled only for executable enable-state operations. Do not add version-based fallback to the older node-specific parameter setters.

Propagate driver failures, including node-association failures, through the normal CUDAError handling path and roll back newly appended owners when the driver rejects an update.

Whole-update integration

After successful Graph.update(source):

Whole-update failure leaves the executable and its active accumulator unchanged.

Acceptance criteria

  • Every supported node type accepts a complete update and launches successfully on CUDA 12.2+.
  • Each public update results in one cuGraphExecNodeSetParams call.
  • No source-identity mirror, eager parameter snapshot, or executable parameter cache is created.
  • Source-definition parameter changes are never mistaken for executable state.
  • Invalid updates leave executable parameters and accumulated ownership unchanged.
  • Required owners survive every launch that may use them.
  • Raw addresses remain caller-owned.
  • The executable does not keep its source CUgraph alive when no source nodes or views remain.
  • Retained source nodes and ephemeral views remain usable after the source graph wrapper is discarded.
  • Unrelated, removed, or otherwise rejected nodes produce the normal error path without retaining replacement owners.
  • Whole updates accept nodes according to CUDA's executable-node association rules.
  • Superseded owners are reclaimed after a successful whole update or final exec destruction once CUDA releases in-flight references.
  • Multiple executable graphs maintain independent accumulators.
  • Documentation explains source lifetime, CUDA-authoritative node validation, the complete-parameter requirement, the lack of executable parameter getters, append-only owner retention, and the CUDA 12.2+ requirement.
  • Kernel, memcpy, and memset views report and change their executable enable state without a cache.

Non-goals

  • Retaining, tracking, or duplicating source-graph identity in cuda.core.
  • CUDA 12.0/12.1 fallback through typed node setters.
  • Partial executable-node updates.
  • Reading current executable-node parameter values.
  • Property setters as mutation syntax.
  • Explicit removal of accumulated owners.
  • Coherent state after mutation through raw CUDA handles.

Metadata

Metadata

Assignees

Labels

P0High priority - Must do!cuda.coreEverything related to the cuda.core modulefeatureNew feature or request

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions