1212 from cuda .core .experimental ._stream import Stream
1313from cuda .core .experimental ._utils .cuda_utils import (
1414 driver ,
15+ get_binding_version ,
1516 handle_return ,
1617)
1718
19+ _inited = False
20+ _driver_ver = None
21+
22+
23+ def _lazy_init ():
24+ global _inited
25+ if _inited :
26+ return
27+
28+ global _py_major_ver , _driver_ver
29+ # binding availability depends on cuda-python version
30+ _py_major_ver , _ = get_binding_version ()
31+ _driver_ver = handle_return (driver .cuDriverGetVersion ())
32+ _inited = True
33+
1834
1935@dataclass
2036class DebugPrintOptions :
@@ -115,23 +131,34 @@ class GraphBuilder:
115131 """
116132
117133 class _MembersNeededForFinalize :
118- __slots__ = ("stream" , "is_stream_owner" , "graph" , "is_conditional " , "is_join_required" )
134+ __slots__ = ("stream" , "is_stream_owner" , "graph" , "conditional_graph " , "is_join_required" )
119135
120- def __init__ (self , graph_builder_obj , stream_obj , is_stream_owner , graph , is_conditional , is_join_required ):
136+ def __init__ (self , graph_builder_obj , stream_obj , is_stream_owner , conditional_graph , is_join_required ):
121137 self .stream = stream_obj
122138 self .is_stream_owner = is_stream_owner
123- self .graph = graph
124- self .is_conditional = is_conditional
139+ self .graph = None
140+ self .conditional_graph = conditional_graph
125141 self .is_join_required = is_join_required
126142 weakref .finalize (graph_builder_obj , self .close )
127143
128144 def close (self ):
129- if self .graph and not self .is_conditional :
145+ if self .stream :
146+ if not self .is_join_required :
147+ capture_status , _ , _ , _ , _ = handle_return (driver .cuStreamGetCaptureInfo (self .stream .handle ))
148+ if capture_status != driver .CUstreamCaptureStatus .CU_STREAM_CAPTURE_STATUS_NONE :
149+ # Note how this condition only occures for the primary graph builder
150+ # This is because calling cuStreamEndCapture streams that were split off of the primary
151+ # would error out with CUDA_ERROR_STREAM_CAPTURE_UNJOINED.
152+ # Therefore, it is currently a requirement that users join all split graph builders
153+ # before a graph builder can be clearly destroyed.
154+ handle_return (driver .cuStreamEndCapture (self .stream .handle ))
155+ if self .is_stream_owner :
156+ self .stream .close ()
157+ self .stream = None
158+ if self .graph :
130159 handle_return (driver .cuGraphDestroy (self .graph ))
131160 self .graph = None
132- if self .is_stream_owner and self .stream :
133- self .stream .close ()
134- self .stream = None
161+ self .conditional_graph = None
135162
136163 __slots__ = ("__weakref__" , "_mnff" , "_building_ended" )
137164
@@ -142,14 +169,13 @@ def __init__(self):
142169 )
143170
144171 @classmethod
145- def _init (cls , stream , is_stream_owner , graph = None , is_conditional = False , is_join_required = False ):
172+ def _init (cls , stream , is_stream_owner , conditional_graph = None , is_join_required = False ):
146173 self = cls .__new__ (cls )
174+ _lazy_init ()
147175 self ._mnff = GraphBuilder ._MembersNeededForFinalize (
148- self , stream , is_stream_owner , graph , is_conditional , is_join_required
176+ self , stream , is_stream_owner , conditional_graph , is_join_required
149177 )
150178
151- if not self ._mnff .graph :
152- self ._mnff .graph = handle_return (driver .cuGraphCreate (0 ))
153179 self ._building_ended = False
154180 return self
155181
@@ -167,16 +193,23 @@ def begin_building(self) -> GraphBuilder:
167193 """Begins the building process."""
168194 if self ._building_ended :
169195 raise RuntimeError ("Cannot resume building after building has ended." )
170- handle_return (
171- driver .cuStreamBeginCaptureToGraph (
172- self ._mnff .stream .handle ,
173- self ._mnff .graph ,
174- None , # dependencies
175- None , # dependencyData
176- 0 , # numDependencies
177- driver .CUstreamCaptureMode .CU_STREAM_CAPTURE_MODE_GLOBAL ,
196+ if self ._mnff .conditional_graph :
197+ handle_return (
198+ driver .cuStreamBeginCaptureToGraph (
199+ self ._mnff .stream .handle ,
200+ self ._mnff .conditional_graph ,
201+ None , # dependencies
202+ None , # dependencyData
203+ 0 , # numDependencies
204+ driver .CUstreamCaptureMode .CU_STREAM_CAPTURE_MODE_GLOBAL ,
205+ )
206+ )
207+ else :
208+ handle_return (
209+ driver .cuStreamBeginCapture (
210+ self ._mnff .stream .handle , driver .CUstreamCaptureMode .CU_STREAM_CAPTURE_MODE_GLOBAL
211+ )
178212 )
179- )
180213 return self
181214
182215 @property
@@ -199,7 +232,10 @@ def end_building(self) -> GraphBuilder:
199232 """Ends the building process."""
200233 if not self .is_building :
201234 raise RuntimeError ("Graph builder is not building." )
202- self ._mnff .graph = handle_return (driver .cuStreamEndCapture (self .stream .handle ))
235+ if self ._mnff .conditional_graph :
236+ self ._mnff .conditional_graph = handle_return (driver .cuStreamEndCapture (self .stream .handle ))
237+ else :
238+ self ._mnff .graph = handle_return (driver .cuStreamEndCapture (self .stream .handle ))
203239
204240 # TODO: Resolving https://github.com/NVIDIA/cuda-python/issues/617 would allow us to
205241 # resume the build process after the first call to end_building()
@@ -336,9 +372,7 @@ def split(self, count) -> Tuple[GraphBuilder, ...]:
336372 stream = self ._mnff .stream .device .create_stream ()
337373 stream .wait (event )
338374 result .append (
339- GraphBuilder ._init (
340- stream = stream , is_stream_owner = True , graph = None , is_conditional = False , is_join_required = True
341- )
375+ GraphBuilder ._init (stream = stream , is_stream_owner = True , conditional_graph = None , is_join_required = True )
342376 )
343377 event .close ()
344378 return result
@@ -407,16 +441,21 @@ def create_conditional_handle(self, default_value=None) -> int:
407441 The newly created conditional handle.
408442
409443 """
444+ if _driver_ver < 12030 :
445+ raise RuntimeError (f"Driver version { _driver_ver } does not support conditional handles" )
410446 if default_value is not None :
411447 flags = driver .CU_GRAPH_COND_ASSIGN_DEFAULT
412448 else :
413449 default_value = 0
414450 flags = 0
451+
452+ status , _ , graph , _ , _ = handle_return (driver .cuStreamGetCaptureInfo (self ._mnff .stream .handle ))
453+ if status != driver .CUstreamCaptureStatus .CU_STREAM_CAPTURE_STATUS_ACTIVE :
454+ raise RuntimeError ("Cannot create a conditional handle when graph is not being built" )
455+
415456 return int (
416457 handle_return (
417- driver .cuGraphConditionalHandleCreate (
418- self ._mnff .graph , self ._get_conditional_context (), default_value , flags
419- )
458+ driver .cuGraphConditionalHandleCreate (graph , self ._get_conditional_context (), default_value , flags )
420459 )
421460 )
422461
@@ -447,8 +486,7 @@ def _cond_with_params(self, node_params) -> GraphBuilder:
447486 GraphBuilder ._init (
448487 stream = self ._mnff .stream .device .create_stream (),
449488 is_stream_owner = True ,
450- graph = node_params .conditional .phGraph_out [i ],
451- is_conditional = True ,
489+ conditional_graph = node_params .conditional .phGraph_out [i ],
452490 is_join_required = False ,
453491 )
454492 for i in range (node_params .conditional .size )
@@ -474,6 +512,8 @@ def if_cond(self, handle: int) -> GraphBuilder:
474512 The newly created conditional graph builder.
475513
476514 """
515+ if _driver_ver < 12030 :
516+ raise RuntimeError (f"Driver version { _driver_ver } does not support conditional if" )
477517 node_params = driver .CUgraphNodeParams ()
478518 node_params .type = driver .CUgraphNodeType .CU_GRAPH_NODE_TYPE_CONDITIONAL
479519 node_params .conditional .handle = handle
@@ -501,6 +541,8 @@ def if_else(self, handle: int) -> Tuple[GraphBuilder, GraphBuilder]:
501541 A tuple of two new graph builders, one for the if branch and one for the else branch.
502542
503543 """
544+ if _driver_ver < 12080 :
545+ raise RuntimeError (f"Driver version { _driver_ver } does not support conditional if-else" )
504546 node_params = driver .CUgraphNodeParams ()
505547 node_params .type = driver .CUgraphNodeType .CU_GRAPH_NODE_TYPE_CONDITIONAL
506548 node_params .conditional .handle = handle
@@ -531,6 +573,8 @@ def switch(self, handle: int, count: int) -> Tuple[GraphBuilder, ...]:
531573 A tuple of new graph builders, one for each branch.
532574
533575 """
576+ if _driver_ver < 12080 :
577+ raise RuntimeError (f"Driver version { _driver_ver } does not support conditional switch" )
534578 node_params = driver .CUgraphNodeParams ()
535579 node_params .type = driver .CUgraphNodeType .CU_GRAPH_NODE_TYPE_CONDITIONAL
536580 node_params .conditional .handle = handle
@@ -558,6 +602,8 @@ def while_loop(self, handle: int) -> GraphBuilder:
558602 The newly created while loop graph builder.
559603
560604 """
605+ if _driver_ver < 12030 :
606+ raise RuntimeError (f"Driver version { _driver_ver } does not support conditional while loop" )
561607 node_params = driver .CUgraphNodeParams ()
562608 node_params .type = driver .CUgraphNodeType .CU_GRAPH_NODE_TYPE_CONDITIONAL
563609 node_params .conditional .handle = handle
0 commit comments