|
1 | 1 | #include "quadrants/runtime/cuda/kernel_launcher.h" |
2 | 2 | #include "quadrants/runtime/cuda/cuda_utils.h" |
3 | 3 | #include "quadrants/rhi/cuda/cuda_context.h" |
| 4 | +#include "quadrants/rhi/cuda/cuda_stream_pin.h" |
4 | 5 | #include "quadrants/runtime/llvm/llvm_runtime_executor.h" |
5 | 6 | #include "quadrants/program/adstack_size_expr_eval.h" |
6 | 7 | #include "quadrants/program/program.h" |
@@ -208,6 +209,18 @@ void KernelLauncher::launch_llvm_kernel(Handle handle, LaunchContextBuilder &ctx |
208 | 209 |
|
209 | 210 | CUDAContext::get_instance().make_current(); |
210 | 211 |
|
| 212 | + // Pin `CUDAContext::stream_` to the legacy default stream (nullptr) for the duration of the launch. The HtoD copies |
| 213 | + // below are sync `cuMemcpyHtoD` (legacy null stream), the kernel dispatches via `cuda_module->launch` go through |
| 214 | + // `CUDAContext::stream_` (now nullptr), and the post-kernel async + sync DtoH calls also land on the null stream. |
| 215 | + // With every operation pinned to one stream, the cross-stream-visibility break that pre-Ampere shows for |
| 216 | + // `cuMemAllocAsync`-pool buffers (Turing T4 faults at `cuLaunchKernel` with `illegal-address` when consumer + |
| 217 | + // producer streams differ) is eliminated unconditionally, and the explicit `stream_synchronize(nullptr)` barriers |
| 218 | + // between phases below collapse to no-ops paid for by the surrounding sync DtoH calls. Same RAII pattern as the |
| 219 | + // Same RAII pattern as the `CudaDefaultStreamPinGuard` defined in `rhi/cuda/cuda_stream_pin.h`; engaging it here at |
| 220 | + // launcher scope covers the HtoD/kernel/DtoH chain that the per-helper guards inside the adstack subsystem do not |
| 221 | + // span. |
| 222 | + CudaDefaultStreamPinGuard cuda_pin(/*engage=*/true); |
| 223 | + |
211 | 224 | // |transfers| is only used for external arrays whose data is originally on |
212 | 225 | // host. They are first transferred onto device and that device pointer is |
213 | 226 | // stored in |device_ptrs| below. |transfers| saves its original pointer so |
@@ -309,9 +322,10 @@ void KernelLauncher::launch_llvm_kernel(Handle handle, LaunchContextBuilder &ctx |
309 | 322 | } |
310 | 323 | } |
311 | 324 | } |
312 | | - if (transfers.size() > 0) { |
313 | | - CUDADriver::get_instance().stream_synchronize(nullptr); |
314 | | - } |
| 325 | + // No explicit `stream_synchronize(nullptr)` needed here. The HtoD copies above are sync `cuMemcpyHtoD` (host-blocking |
| 326 | + // by definition); with the launcher's `CudaDefaultStreamPinGuard` ensuring the subsequent kernel dispatch lands on |
| 327 | + // the same null stream, ordering between HtoD and kernel is preserved without an explicit barrier even on pre-Ampere |
| 328 | + // pool buffers - the cross-stream visibility break only fires when producer + consumer streams differ. |
315 | 329 | char *host_result_buffer = (char *)ctx.get_context().result_buffer; |
316 | 330 | if (ctx.result_buffer_size > 0) { |
317 | 331 | ctx.get_context().result_buffer = (uint64 *)device_result_buffer; |
@@ -380,9 +394,11 @@ void KernelLauncher::launch_llvm_kernel(Handle handle, LaunchContextBuilder &ctx |
380 | 394 | CUDADriver::get_instance().memcpy_device_to_host_async(host_result_buffer, device_result_buffer, |
381 | 395 | ctx.result_buffer_size, nullptr); |
382 | 396 | } |
383 | | - // copy data back to host |
| 397 | + // copy data back to host. With the launcher-scope `CudaDefaultStreamPinGuard` the kernel ran on the null stream, so |
| 398 | + // sync `cuMemcpyDtoH` (host-blocking, on null stream) sees the kernel's writes without an explicit cross-stream |
| 399 | + // barrier. The first sync DtoH below also drains the prior async `memcpy_device_to_host_async(host_result_buffer, |
| 400 | + // ...)` queued on the same null stream, so an explicit `stream_synchronize(nullptr)` here would be redundant. |
384 | 401 | if (transfers.size() > 0) { |
385 | | - CUDADriver::get_instance().stream_synchronize(nullptr); |
386 | 402 | for (auto itr = transfers.begin(); itr != transfers.end(); itr++) { |
387 | 403 | auto &idx = itr->first; |
388 | 404 | CUDADriver::get_instance().memcpy_device_to_host(itr->second.first, (void *)device_ptrs[idx], |
|
0 commit comments