@@ -183,6 +183,117 @@ class _FakeDev:
183183 assert attr .value .cooperative == 1 , f"Expected cooperative=1, got { attr .value .cooperative } "
184184
185185
186+ def test_to_native_launch_config_pdl ():
187+ """LaunchConfig(programmatic_stream_serialization=True) maps to the PDL launch attribute."""
188+ from cuda .bindings import driver
189+ from cuda .core ._launch_config import _to_native_launch_config
190+
191+ config = LaunchConfig (grid = 2 , block = 4 , programmatic_stream_serialization = True )
192+ native = _to_native_launch_config (config )
193+ assert native .gridDimX == 2
194+ assert native .blockDimX == 4
195+ assert native .numAttrs == 1
196+ attr = native .attrs [0 ]
197+ assert attr .id == driver .CUlaunchAttributeID .CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION , (
198+ f"Expected CU_LAUNCH_ATTRIBUTE_PROGRAMMATIC_STREAM_SERIALIZATION, got { attr .id } "
199+ )
200+ assert attr .value .programmaticStreamSerializationAllowed == 1 , (
201+ f"Expected programmaticStreamSerializationAllowed=1, got { attr .value .programmaticStreamSerializationAllowed } "
202+ )
203+
204+
205+ @skipif_need_cuda_headers
206+ def test_pdl_primary_secondary_overlap_same_stream ():
207+ """Primary + secondary PDL launch on one stream can overlap on Hopper+.
208+
209+ Secondary is launched with ``programmatic_stream_serialization=True``. After
210+ the primary triggers completion, it spins until it observes a flag written by
211+ the secondary's independent preamble — proving both grids were resident at
212+ once. Without PDL, the secondary cannot start until the primary exits.
213+
214+ Note concurrency is opportunistic, so a missing overlap execution is reported as
215+ an expected failure.
216+ """
217+ dev = Device ()
218+ if dev .compute_capability < (9 , 0 ):
219+ pytest .skip ("Programmatic Dependent Launch requires compute capability >= 9.0" )
220+ dev .set_current ()
221+ stream = dev .create_stream (options = {"nonblocking" : True })
222+
223+ # clock64 budgets are in GPU cycles; keep the post-trigger window long enough
224+ # for the secondary to boot, but short enough for a unit test.
225+ code = r"""
226+ #include <cuda_device_runtime_api.h>
227+
228+ extern "C" __global__ void primary_kernel(int* secondary_started, int* overlapped) {
229+ cudaTriggerProgrammaticLaunchCompletion();
230+
231+ const long long deadline = clock64() + 100000000LL; // ~50ms @ ~2GHz
232+ if (threadIdx.x == 0 && blockIdx.x == 0) {
233+ while (clock64() < deadline) {
234+ if (atomicAdd(secondary_started, 0) != 0) {
235+ atomicExch(overlapped, 1);
236+ return;
237+ }
238+ __nanosleep(1000);
239+ }
240+ }
241+ }
242+
243+ extern "C" __global__ void secondary_kernel(int* secondary_started) {
244+ if (threadIdx.x == 0 && blockIdx.x == 0) {
245+ atomicExch(secondary_started, 1);
246+ }
247+ }
248+ """
249+
250+ arch = "" .join (f"{ i } " for i in dev .compute_capability )
251+ pro_opts = ProgramOptions (std = "c++17" , arch = f"sm_{ arch } " , include_path = helpers .CUDA_INCLUDE_PATH )
252+ prog = Program (code , code_type = "c++" , options = pro_opts )
253+ mod = prog .compile ("cubin" )
254+ primary = mod .get_kernel ("primary_kernel" )
255+ secondary = mod .get_kernel ("secondary_kernel" )
256+
257+ mr = LegacyPinnedMemoryResource ()
258+ secondary_started = np .from_dlpack (mr .allocate (4 )).view (np .int32 )
259+ overlapped = np .from_dlpack (mr .allocate (4 )).view (np .int32 )
260+
261+ primary_cfg = LaunchConfig (grid = 1 , block = 1 )
262+ secondary_cfg = LaunchConfig (grid = 1 , block = 1 , programmatic_stream_serialization = True )
263+ secondary_serial_cfg = LaunchConfig (grid = 1 , block = 1 )
264+
265+ def _run (secondary_launch_cfg : LaunchConfig ) -> int :
266+ secondary_started [0 ] = 0
267+ overlapped [0 ] = 0
268+ launch (stream , primary_cfg , primary , secondary_started .ctypes .data , overlapped .ctypes .data )
269+ launch (stream , secondary_launch_cfg , secondary , secondary_started .ctypes .data )
270+ stream .sync ()
271+ return int (overlapped [0 ])
272+
273+ # Without the PDL attribute, same-stream kernels stay serialized.
274+ assert _run (secondary_serial_cfg ) == 0 , "Expected no overlap when programmatic_stream_serialization is False"
275+
276+ # PDL overlap is opportunistic; retry a few times on a quiet GPU.
277+ saw_overlap = False
278+ for _ in range (5 ):
279+ if _run (secondary_cfg ) == 1 :
280+ saw_overlap = True
281+ break
282+
283+ if not saw_overlap :
284+ # Overlap is never guaranteed by the driver, so a miss is reported as an
285+ # expected failure rather than turning a busy GPU into a red CI run.
286+ pytest .xfail (
287+ "PDL (Programmatic Dependent Launch) overlap was not observed. "
288+ "If this keeps xfailing in CI, manually re-check on a quiet Hopper+ GPU."
289+ )
290+
291+ print (
292+ f"PDL (Programmatic Dependent Launch) overlap verified on { dev .name } compute capability { dev .compute_capability } " ,
293+ flush = True ,
294+ )
295+
296+
186297def test_launch_config_cluster_accepts_hopper_cc (monkeypatch ):
187298 """LaunchConfig accepts ``cluster`` when the device reports compute
188299 capability >= 9.0. Device is mocked so the cluster-cast branch runs on any
0 commit comments