Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cuda_core/cuda/core/_module.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ class KernelAttributes:
@property
def cluster_scheduling_policy_preference(self) -> int:
"""int : The block scheduling policy of a function."""
@property
def device_node_update_supported(self) -> bool:
"""bool : Whether this kernel can be the target of device-side graph node updates.

Requires CUDA 13 build and CUDA driver 13.3+.
This attribute is read-only.
"""

class KernelOccupancy:
"""This class offers methods to query occupancy metrics that help determine optimal
Expand Down
17 changes: 17 additions & 0 deletions cuda_core/cuda/core/_module.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,23 @@ cdef class KernelAttributes:
cydriver.CU_FUNC_ATTRIBUTE_CLUSTER_SCHEDULING_POLICY_PREFERENCE,
)

@property
def device_node_update_supported(self) -> bool:
"""bool : Whether this kernel can be the target of device-side graph node updates.

Requires CUDA 13 build and CUDA driver 13.3+.
This attribute is read-only.
"""
IF CUDA_CORE_BUILD_MAJOR >= 13:
if cy_driver_version() < (13, 3, 0):
raise NotImplementedError("Device node update queries require CUDA driver 13.3 or newer")
return bool(self._get_cached_attribute(
self._effective_device_id(),
cydriver.CU_FUNC_ATTRIBUTE_DEVICE_NODE_UPDATE_SUPPORTED,
))
ELSE:
raise NotImplementedError("Device node update queries require CUDA driver 13.3 or newer")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

important: Include the CUDA 13 build requirement in the error.

On a CUDA 12 build with a 13.3 or newer driver, this error says a newer driver is required even though the driver meets that requirement. State that the property also requires a CUDA 13 build.



MaxPotentialBlockSizeOccupancyResult = namedtuple(
"MaxPotentialBlockSizeOccupancyResult", ("min_grid_size", "max_block_size")
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/cuda/core/graph/_graph_builder.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,12 @@ class Graph:
def is_closed(self) -> bool:
"""Whether this executable graph has been closed."""
@property
def id(self) -> int:
"""The driver-assigned executable graph ID, as used in GraphViz DOT output.

Requires CUDA >= 13.1.
"""
@property
def handle(self) -> driver.CUgraphExec:
"""Return the underlying ``CUgraphExec`` object.

Expand Down
18 changes: 18 additions & 0 deletions cuda_core/cuda/core/graph/_graph_builder.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,24 @@ cdef class Graph:
"""Whether this executable graph has been closed."""
return self._h_graph_exec.get() == NULL

@property
def id(self) -> int:
"""The driver-assigned executable graph ID, as used in GraphViz DOT output.

Requires CUDA >= 13.1.
"""
if self.is_closed:
raise RuntimeError("Graph is closed")
IF CUDA_CORE_BUILD_MAJOR >= 13:
cdef unsigned int graph_id
if cy_driver_version() < (13, 1, 0):
raise NotImplementedError("Graph IDs require CUDA driver 13.1 or newer")
with nogil:
HANDLE_RETURN(cydriver.cuGraphExecGetId(as_cu(self._h_graph_exec), &graph_id))
return graph_id
ELSE:
raise NotImplementedError("Graph IDs require CUDA driver 13.1 or newer")

@property
def handle(self) -> driver.CUgraphExec:
"""Return the underlying ``CUgraphExec`` object.
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/cuda/core/graph/_graph_definition.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ class GraphDefinition:
def __eq__(self, other: object) -> bool: ...
def __hash__(self) -> int: ...
@property
def id(self) -> int:
"""The driver-assigned graph ID, as used in GraphViz DOT output.

Requires CUDA 13 build and driver 13.1+
"""
@property
def is_valid(self) -> bool:
"""Whether this graph definition remains valid."""
@property
Expand Down
19 changes: 19 additions & 0 deletions cuda_core/cuda/core/graph/_graph_definition.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ from cuda.core._rt cimport (
create_graph_node_handle,
)
from cuda.core._utils.cuda_utils cimport HANDLE_RETURN
IF CUDA_CORE_BUILD_MAJOR >= 13:
from cuda.core._utils.version cimport cy_driver_version

from typing import TYPE_CHECKING

Expand Down Expand Up @@ -145,6 +147,23 @@ cdef class GraphDefinition:
def __hash__(self) -> int:
return hash(<uintptr_t>self._h_graph.get())

@property
def id(self) -> int:
"""The driver-assigned graph ID, as used in GraphViz DOT output.

Requires CUDA 13 build and driver 13.1+
"""
GD_check_valid(self)
IF CUDA_CORE_BUILD_MAJOR >= 13:
cdef unsigned int graph_id
if cy_driver_version() < (13, 1, 0):
raise NotImplementedError("Graph IDs require CUDA driver 13.1 or newer")
with nogil:
HANDLE_RETURN(cydriver.cuGraphGetId(as_cu(self._h_graph), &graph_id))
return graph_id
ELSE:
raise NotImplementedError("Graph IDs require CUDA driver 13.1 or newer")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

important: State the build requirement in the unsupported-build error.

On a CUDA 12 build, GraphDefinition.id raises an error that names only driver 13.1. A user with that driver still cannot use the property. Name both the CUDA 13 build and driver 13.1 requirements, as the property docstring does.


@property
def is_valid(self) -> bool:
"""Whether this graph definition remains valid."""
Expand Down
12 changes: 12 additions & 0 deletions cuda_core/cuda/core/graph/_graph_node.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ class GraphNode:
The node type enum value, or None for the entry node.
"""
@property
def local_id(self) -> int:
"""The node's ID within its graph.

Requires CUDA 13 build and driver 13.1+
"""
@property
def tools_id(self) -> int:
"""The node's ID reported by CUDA profiling and tooling interfaces.

Requires CUDA 13 build and driver 13.1+
"""
@property
def graph(self) -> GraphDefinition:
"""Return the GraphDefinition this node belongs to."""
@property
Expand Down
40 changes: 40 additions & 0 deletions cuda_core/cuda/core/graph/_graph_node.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ from cuda.core._rt cimport (
make_opaque_py,
)
from cuda.core._utils.cuda_utils cimport HANDLE_RETURN, _parse_fill_value
IF CUDA_CORE_BUILD_MAJOR >= 13:
from cuda.core._utils.version cimport cy_driver_version

from cuda.core._host_callback cimport (
_resolve_host_callback,
Expand Down Expand Up @@ -145,6 +147,44 @@ cdef class GraphNode:
HANDLE_RETURN(cydriver.cuGraphNodeGetType(node, &node_type))
return driver.CUgraphNodeType(<int>node_type)

@property
def local_id(self) -> int:
"""The node's ID within its graph.

Requires CUDA 13 build and driver 13.1+
"""
GN_check_valid(self)
if self._is_entry:
raise ValueError("The entry node has no driver-assigned ID")
IF CUDA_CORE_BUILD_MAJOR >= 13:
cdef unsigned int node_id
if cy_driver_version() < (13, 1, 0):
raise NotImplementedError("Graph IDs require a CUDA 13 build of cuda.core and CUDA driver 13.1 or newer")
with nogil:
HANDLE_RETURN(cydriver.cuGraphNodeGetLocalId(as_cu(self._h_node), &node_id))
return node_id
ELSE:
raise NotImplementedError("Graph IDs require a CUDA 13 build of cuda.core and CUDA driver 13.1 or newer")

@property
def tools_id(self) -> int:
"""The node's ID reported by CUDA profiling and tooling interfaces.

Requires CUDA 13 build and driver 13.1+
"""
GN_check_valid(self)
if self._is_entry:
raise ValueError("The entry node has no driver-assigned ID")
IF CUDA_CORE_BUILD_MAJOR >= 13:
cdef unsigned long long node_id
if cy_driver_version() < (13, 1, 0):
raise NotImplementedError("Graph IDs require a CUDA 13 build of cuda.core and CUDA driver 13.1 or newer")
with nogil:
HANDLE_RETURN(cydriver.cuGraphNodeGetToolsId(as_cu(self._h_node), &node_id))
return node_id
ELSE:
raise NotImplementedError("Graph IDs require a CUDA 13 build of cuda.core and CUDA driver 13.1 or newer")

@property
def graph(self) -> GraphDefinition:
"""Return the GraphDefinition this node belongs to."""
Expand Down
9 changes: 9 additions & 0 deletions cuda_core/docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ CPU overhead. Graphs can be constructed in two ways:
edges. Both produce an executable :class:`~graph.Graph` that can be
launched on a :class:`Stream`.

With CUDA >= 13.1, graph definitions and

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Functional Correctness · Quick win

suggestion: State the CUDA 13 build requirement for these properties. On a CUDA 12 build, the ID properties raise NotImplementedError even with a 13.1-or-newer driver. device_node_update_supported also raises on that build even with a 13.3-or-newer driver. State the build requirement in both paragraphs and in the Graph.id declaration.

As per coding guidelines, “Keep user-facing behaviors coherent with docs and samples.” As per path instructions, “Check that public behavior changes have corresponding tests, documentation and release notes.”

Sources: Coding guidelines, Path instructions

executable graphs expose a read-only integer, ``id``. Nodes expose ``local_id``
(unique within their graph) and ``tools_id`` (for profiler correlation).
Use ``node.graph`` to obtain the containing graph definition; this view
shares ownership of the underlying graph. IDs are ephemeral, driver-assigned.

``kernel.attributes.device_node_update_supported`` reports whether a kernel
supports device-side node updates. It requires CUDA >= 13.3.

.. currentmodule:: cuda.core

.. autosummary::
Expand Down
6 changes: 6 additions & 0 deletions cuda_core/docs/source/release/1.3.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Breaking Changes
New features
------------

- Added read-only ``GraphDefinition.id``, ``Graph.id``, ``GraphNode.local_id``
and ``GraphNode.tools_id`` properties (CUDA 13 build and driver 13.1+), and
``KernelAttributes.device_node_update_supported`` (CUDA 13 build and driver 13.3+).
The existing ``GraphNode.graph`` property provides containing-graph navigation.
(`#2360 <https://github.com/NVIDIA/cuda-python/issues/2360>`__)

- Added :func:`host_launch`, which enqueues a host function on a stream in stream
order: a Python callable, or a ctypes function pointer with the ``CUhostFn``
prototype, which ``cuda.core`` passes to the driver as is, so a pointer to
Expand Down
28 changes: 28 additions & 0 deletions cuda_core/tests/graph/test_graph_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,34 @@ def test_graphdef_handle_valid(init_cuda):
assert int(sample_graphdef.handle) != 0


def test_graph_identity_attributes(init_cuda):
from cuda.core._utils.version import BUILD_CUDA_MAJOR, driver_version

definition = GraphDefinition()
node = definition.empty()
other = definition.empty()
graph = definition.instantiate()

try:
if BUILD_CUDA_MAJOR < 13 or driver_version() < (13, 1, 0):
with pytest.raises(NotImplementedError):
_ = definition.id
with pytest.raises(NotImplementedError):
_ = graph.id
with pytest.raises(NotImplementedError):
_ = node.local_id
with pytest.raises(NotImplementedError):
_ = node.tools_id
return
assert isinstance(definition.id, int)
assert isinstance(graph.id, int)
assert node.graph == definition
assert node.local_id != other.local_id
assert node.tools_id != other.tools_id
finally:
graph.close()


def test_graphdef_entry_is_virtual(init_cuda):
"""Internal entry node is virtual (no pred/succ, type is None)."""
sample_graphdef = GraphDefinition()
Expand Down
13 changes: 13 additions & 0 deletions cuda_core/tests/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ def test_read_only_kernel_attributes(get_saxpy_kernel_cubin, attr, expected_type
assert isinstance(value, expected_type), f"Expected {attr} to be of type {expected_type}, but got {type(value)}"


def test_device_node_update_supported(get_saxpy_kernel_cubin):
from cuda.core._utils.version import BUILD_CUDA_MAJOR, driver_version

kernel, _ = get_saxpy_kernel_cubin

for device in Device.get_all_devices():
if BUILD_CUDA_MAJOR < 13 or driver_version() < (13, 3, 0):
with pytest.raises(NotImplementedError):
_ = kernel.attributes[device].device_node_update_supported
else:
assert isinstance(kernel.attributes[device].device_node_update_supported, bool)


def test_object_code_load_ptx(get_saxpy_kernel_ptx):
ptx, mod = get_saxpy_kernel_ptx
sym_map = mod.symbol_mapping
Expand Down
Loading