Add unit tests and type-safe overloads for CUDA kernel functions#800
Open
saschiwy wants to merge 1 commit into
Open
Add unit tests and type-safe overloads for CUDA kernel functions#800saschiwy wants to merge 1 commit into
saschiwy wants to merge 1 commit into
Conversation
1d72ba5 to
47bedd8
Compare
Includes tests for compile-time argument checks, scalar widening, pointer qualification, and backward compatibility. Negative compile tests validate incompatible arguments fail correctly. Updated CMakeLists to include new tests.
47bedd8 to
0adfa62
Compare
Author
|
Sorry, the commits went out with my work email — I'm rewriting the history to use my personal address and will force-push. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Add type-safe
kernel()overloads tocudaGraphBaseandcudaGraphExecBaseAcknowledgement
First and foremost: a huge thank you to Dr. Tsung-Wei Huang and all contributors for building
and maintaining Taskflow. It is one of the most elegant and well-engineered C++ frameworks I
have encountered, and I rely on it in production code daily.
Background
I use Taskflow in a production pipeline that runs on both Linux and Windows. Over time I ran
into a platform-dependent crash that took a while to track down. The root cause turned out to
be a completely silent type mismatch in a
cudaGraph::kernel()call:On Linux this worked fine — GCC's stack layout happens to zero the upper 4 bytes of the
8-byte
size_tslot, so the driver reads the correct value by accident. On Windows the upperbytes contain garbage, and the CUDA driver interprets the argument as something like
0x????????00000100, causing the kernel to access memory far out of bounds and fail withcudaErrorIllegalAddress.To be clear: this is not a bug in Taskflow. The existing API is intentionally generic and
fully correct within the C++ rules. The mismatch lives entirely at the call site.
That said, I thought others might benefit from having these errors surface at compile time
rather than as hard-to-reproduce runtime crashes — especially in cross-platform codebases
where the Linux/Windows ABI difference means the bug is invisible during development and only
shows up in production.
What this PR adds
A new overload of
kernel()for bothcudaGraphBaseandcudaGraphExecBasethat accepts atyped
__global__function pointer (void(*)(Params...)) instead of the generictemplate<typename F>callable. Because the function pointer carries the exact parametertypes in its type, the compiler can enforce correct argument types before anything is packed
into the
void*array.New helper:
tf::detail::kernelArgCastTwo cast strategies are used deliberately:
static_cast: A narrower type (e.g.int) being passed where a wider type(e.g.
size_t) is expected results in a widened, correct value. A genuinely incompatibletype (a struct, an unrelated enum) produces a compile error.
reinterpret_cast: Required to handle two common real-world cases thatstatic_castrejects in CUDA's augmented type system:T*→const T*const-qualification (nvcc rejects this viastatic_cast)typedef bool boolean_Tvs__nv_bool*that code generators produceNew overload in
cudaGraphBase(cuda_graph.hpp)The cast values are stored in a
std::tuple— not addressed directly from the functionparameter pack — to guarantee that the
void*pointers remain valid for the duration ofthe
cudaGraphAddKernelNodecall. This avoids a subtle dangling-address issue that theoriginal naive pattern (
void* arguments[] = { (void*)(&args)... }) is susceptible to whena conversion creates a temporary.
New overload in
cudaGraphExecBase(cuda_graph_exec.hpp)Identical pattern, calling
cudaGraphExecKernelNodeSetParamsinstead.Overload resolution — no ambiguity, no breaking changes
The two overloads coexist cleanly:
When the caller passes a named
__global__function, its type isvoid(*)(Params...)—Overload B matches more specifically via partial ordering (
[temp.func.order]) and ispreferred. When the caller passes a lambda or functor, deduction for Overload B fails and
only Overload A is viable. No existing call site needs to change.
What this catches (and what it doesn't)
intpassed wheresize_texpectedunsigned intpassed wheresize_texpectedstatic_assert)float*passed whereconst float*expectedboolean_T*(typedef) passed where__nv_bool*expectedkernel()float*passed whereint*expectedPointer-to-incompatible-type mismatches (
float*forint*) are intentionally outsidethe protection boundary; they require
compute-sanitizeror code review to detect.Tests
A new test file
unittests/cuda/test_cuda_type_safe_kernel.cuis added with 11 test cases:kernelArgCastdispatch: scalar path usesstatic_cast, pointer path usesreinterpret_cast(100 iterations each)intpassed forsize_t N, GPU output verified across 100+ size valuescudaGraphExecBase::kernel()const-qualification, argument count match, backward-compat with named function pointers, exec-path variantsTwo negative-compile test files (
test_compile_fail_arg_count.cu,test_compile_fail_scalar_mismatch.cu) are wired intoCMakeLists.txtviatry_compiletoverify the
static_assertand ill-formedstatic_castpaths actually reject bad code.Files changed
taskflow/cuda/cuda_graph.hpptf::detail::kernelArgCast; add typedkernel()overload tocudaGraphBasetaskflow/cuda/cuda_graph_exec.hppkernel()overload tocudaGraphExecBaseunittests/cuda/test_cuda_type_safe_kernel.cuunittests/cuda/test_compile_fail_arg_count.cuunittests/cuda/test_compile_fail_scalar_mismatch.cuunittests/cuda/CMakeLists.txttry_compilenegative testsdoxygen/releases/release-4.2.0.dox