-
Notifications
You must be signed in to change notification settings - Fork 275
Graph node follow-ups: repr, containment, empty(), registry docs #1859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
dc92437
Reorganize graph test files for clarity
Andy-Jost 281ed82
Enhance Graph.update() and add whole-graph update tests
Andy-Jost 7854b76
Add AdjacencySet proxy for pred/succ and GraphNode.remove()
Andy-Jost 5fbd288
Add edge mutation support and MutableSet interface for GraphNode adja…
Andy-Jost aa84e26
Use requires_module mark for numpy version checks in mutation tests
Andy-Jost b27dd93
Fix empty-graph return type: return set() instead of () for nodes/edges
Andy-Jost 8554d30
Rename AdjacencySet to AdjacencySetProxy, add bulk ops and safety guards
Andy-Jost 9813c20
Add destroy() method with handle invalidation, remove GRAPH_NODE_SENT…
Andy-Jost 6411881
Add GraphNode identity cache for stable Python object round-trips
Andy-Jost 7a3dbb4
Purge node cache on destroy to prevent stale identity lookups
Andy-Jost 91b3b4e
Skip NULL nodes in graph_node_registry to fix sentinel identity colli…
Andy-Jost 1b7743d
Unregister destroyed nodes from C++ graph_node_registry
Andy-Jost 84f0b30
Add dedicated test for node identity preservation through round-trips
Andy-Jost 64d6c2d
Merge branch 'main' into graph-node-identity
Andy-Jost 6b36e47
Add handle= to all GraphNode subclass __repr__ for debugging
Andy-Jost a40be9a
Merge branch 'main' into graph-node-identity
Andy-Jost 729af49
Rename _node_cache/_cached to _node_registry/_registered
Andy-Jost 42131b6
Fix unregister_handle and rename invalidate_graph_node_handle
Andy-Jost 9766e54
Merge branch 'graph-node-identity' into graph-node-repr
Andy-Jost 15d0036
Add cheap containment test and early type check for AdjacencySetProxy
Andy-Jost 347693f
Add GraphDef.empty(), stack-buffer query optimization, and registry test
Andy-Jost 641a089
Document the two-level handle and object registry design
Andy-Jost 8370687
Fix import formatting in test_registry_cleanup
Andy-Jost 36527da
Merge origin/main into graph-node-repr
Andy-Jost f779f30
Optimize GraphDef.nodes() and edges() to try a single driver call
Andy-Jost File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Handle and Object Registries | ||
|
|
||
| When Python-managed objects round-trip through the CUDA driver (e.g., | ||
| querying a graph's nodes and getting back raw `CUgraphNode` pointers), | ||
| we need to recover the original Python object rather than creating a | ||
| duplicate. | ||
|
|
||
| This document describes the approach used to achieve this. The pattern | ||
| is driven mainly by needs arising in the context of CUDA graphs, but | ||
| it is general and can be extended to other object types as needs arise. | ||
|
|
||
| This solves the same problem as pybind11's `registered_instances` map | ||
| and is sometimes called the Identity Map pattern. Two registries work | ||
| together to map a raw driver handle all the way back to the original | ||
| Python object. Both use weak references so they | ||
| do not prevent cleanup. Entries are removed either explicitly (via | ||
| `destroy()` or a Box destructor) or implicitly when the weak reference | ||
| expires. | ||
|
|
||
| ## Level 1: Driver Handle -> Resource Handle (C++) | ||
|
|
||
| `HandleRegistry` in `resource_handles.cpp` maps a raw CUDA handle | ||
| (e.g., `CUevent`, `CUkernel`, `CUgraphNode`) to the `weak_ptr` that | ||
| owns it. When a `_ref` constructor receives a raw handle, it | ||
| checks the registry first. If found, it returns the existing | ||
| `shared_ptr`, preserving the Box and its metadata (e.g., `EventBox` | ||
| carries timing/IPC flags, `KernelBox` carries the library dependency). | ||
|
|
||
| Without this level, a round-tripped handle would produce a new Box | ||
| with default metadata, losing information that was set at creation. | ||
|
|
||
| Instances: `event_registry`, `kernel_registry`, `graph_node_registry`. | ||
|
|
||
| ## Level 2: Resource Handle -> Python Object (Cython) | ||
|
|
||
| `_node_registry` in `_graph_node.pyx` is a `WeakValueDictionary` | ||
| mapping a resource address (`shared_ptr::get()`) to a Python | ||
| `GraphNode` object. When `GraphNode._create` receives a handle from | ||
| Level 1, it checks this registry. If found, it returns the existing | ||
| Python object. | ||
|
|
||
| Without this level, each driver round-trip would produce a distinct | ||
| Python object for the same logical node, resulting in surprising | ||
| behavior: | ||
|
|
||
| ```python | ||
| a = g.empty() | ||
| a.succ = {b} | ||
| b2, = a.succ # queries driver, gets back CUgraphNode for b | ||
| assert b2 is b # fails without Level 2 registry | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't able to return a generator as suggested here because
nodes_vecis a stack object and it would lead to a use-after-free unless we define a cdef class for the iterator. I added an optimizedcontainsmethod that avoids reconstructing Python objects for containment checks.