Repository navigation
Introduce the target agnostic UNIFIED kernel architecture with nvJitLink - #5722
Merged
Merged
Conversation
Renaud-K
requested review from
1tnguyen,
bettinaheim,
boschmitt and
cketcham2333
and removed request for
cketcham2333
October 24, 2026 01:47
Collaborator
Author
Command Bot: Processing... |
CI Summary (
|
| Job | Result |
|---|---|
binaries |
⏩ skipped |
build_and_test |
✅ success |
changes |
✅ success |
config_devdeps |
✅ success |
config_source_build |
⏩ skipped |
config_wheeldeps |
✅ success |
devdeps |
✅ success |
docker_image |
⏩ skipped |
documentation |
✅ success |
gen_code_coverage |
⏩ skipped |
metadata |
✅ success |
python_devel_wheel |
⏩ skipped |
python_metapackages |
⏩ skipped |
python_wheels |
⏩ skipped |
python_wheels_cu12_validation |
⏩ skipped |
python_wheels_cu13 |
⏩ skipped |
source_build |
⏩ skipped |
wheeldeps |
✅ success |
⏩ Skipped jobs (10) — intentionally skipped on PR builds; run on merge_group / workflow_dispatch
| Job |
|---|
binaries |
config_source_build |
docker_image |
gen_code_coverage |
python_devel_wheel |
python_metapackages |
python_wheels_cu12_validation |
python_wheels_cu13 |
python_wheels |
source_build |
All sub-jobs (47) — every matrix leg, with links
| Job | Status | Link |
|---|---|---|
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| CI Summary | ❔ in_progress | view |
| Check for stable CUDA-Q changes | ✅ success | view |
| Configure build (devdeps) | ✅ success | view |
| Configure build (source_build) | ⏩ skipped | view |
| Configure build (wheeldeps) | ✅ success | view |
| Create CUDA 12 Python wheels | ⏩ skipped | view |
| Create CUDA 13 Python wheels | ⏩ skipped | view |
| Create CUDA Quantum installer | ⏩ skipped | view |
| Create Docker images | ⏩ skipped | view |
| Create Python metapackages | ⏩ skipped | view |
| Create shared Python development wheel | ⏩ skipped | view |
| Documentation / Build and check docs | ✅ success | view |
| Gen code coverage | ⏩ skipped | view |
| Load dependencies (amd64, gcc12) / Caching | ✅ success | view |
| Load dependencies (amd64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (amd64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (amd64, llvm) / Caching | ✅ success | view |
| Load dependencies (amd64, llvm) / Finalize | ✅ success | view |
| Load dependencies (amd64, llvm) / Metadata | ✅ success | view |
| Load dependencies (arm64, gcc12) / Caching | ✅ success | view |
| Load dependencies (arm64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (arm64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (arm64, llvm) / Caching | ✅ success | view |
| Load dependencies (arm64, llvm) / Finalize | ✅ success | view |
| Load dependencies (arm64, llvm) / Metadata | ✅ success | view |
| Load source build cache | ⏩ skipped | view |
| Load wheel dependencies (amd64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Metadata | ✅ success | view |
| Prepare cache clean-up | ❔ in_progress | view |
| Retrieve PR info | ✅ success | view |
| Validate CUDA 12 Python wheels | ⏩ skipped | view |
✅ Required checks (7/7) — declared in .github/required-checks.yml for push
| Required check | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
| Documentation / Build and check docs | ✅ success | view |
Collaborator
Author
Command Bot: Processing... |
Collaborator
Author
Command Bot: Processing... |
Collaborator
Author
Command Bot: Processing... |
Renaud-K
force-pushed
the
gpu-dataplane
branch
from
October 26, 2026 22:59
98011c2 to
0fa37a7
Compare
Collaborator
Author
Command Bot: Processing... |
Renaud-K
commented
Oct 27, 2026
1tnguyen
reviewed
Oct 28, 2026
1tnguyen
reviewed
Oct 28, 2026
Collaborator
|
LGTM 👍 |
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Link CUDA::nvJitLink PRIVATE into cudaq-realtime-device-code, add a ctest that fails if a public header includes nvJitLink.h, and document that only building libcudaq-realtime needs the nvJitLink dev package while applications, bridges and customers need only the runtime libnvJitLink.so.<major>.
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Renaud-K
force-pushed
the
gpu-dataplane
branch
from
October 28, 2026 15:35
0fa37a7 to
121d3a8
Compare
Collaborator
Author
Command Bot: Processing... |
Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
Collaborator
Author
Command Bot: Processing... |
bettinaheim
approved these changes
Oct 29, 2026
CI Summary (
|
| Job | Result |
|---|---|
binaries |
⏩ skipped |
build_and_test |
✅ success |
changes |
✅ success |
config_devdeps |
✅ success |
config_source_build |
⏩ skipped |
config_wheeldeps |
⏩ skipped |
devdeps |
✅ success |
docker_image |
⏩ skipped |
documentation |
⏩ skipped |
gen_code_coverage |
⏩ skipped |
metadata |
✅ success |
python_devel_wheel |
⏩ skipped |
python_metapackages |
⏩ skipped |
python_wheels |
⏩ skipped |
python_wheels_cu12_validation |
⏩ skipped |
python_wheels_cu13 |
⏩ skipped |
source_build |
⏩ skipped |
wheeldeps |
⏩ skipped |
⏩ Skipped jobs (13) — intentionally skipped on PR builds; run on merge_group / workflow_dispatch
| Job |
|---|
binaries |
config_source_build |
config_wheeldeps |
docker_image |
documentation |
gen_code_coverage |
python_devel_wheel |
python_metapackages |
python_wheels_cu12_validation |
python_wheels_cu13 |
python_wheels |
source_build |
wheeldeps |
All sub-jobs (28) — every matrix leg, with links
| Job | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| CI Summary | ❔ in_progress | view |
| Check for stable CUDA-Q changes | ✅ success | view |
| Configure build (devdeps) | ✅ success | view |
| Configure build (source_build) | ⏩ skipped | view |
| Configure build (wheeldeps) | ⏩ skipped | view |
| Create CUDA 12 Python wheels | ⏩ skipped | view |
| Create CUDA 13 Python wheels | ⏩ skipped | view |
| Create CUDA Quantum installer | ⏩ skipped | view |
| Create Docker images | ⏩ skipped | view |
| Create Python metapackages | ⏩ skipped | view |
| Create shared Python development wheel | ⏩ skipped | view |
| Documentation | ⏩ skipped | view |
| Gen code coverage | ⏩ skipped | view |
| Load dependencies (amd64, llvm) / Caching | ✅ success | view |
| Load dependencies (amd64, llvm) / Finalize | ✅ success | view |
| Load dependencies (amd64, llvm) / Metadata | ✅ success | view |
| Load dependencies (arm64, llvm) / Caching | ✅ success | view |
| Load dependencies (arm64, llvm) / Finalize | ✅ success | view |
| Load dependencies (arm64, llvm) / Metadata | ✅ success | view |
| Load source build cache | ⏩ skipped | view |
| Load wheel dependencies | ⏩ skipped | view |
| Prepare cache clean-up | ✅ success | view |
| Retrieve PR info | ✅ success | view |
| Validate CUDA 12 Python wheels | ⏩ skipped | view |
✅ Required checks (4/4) — declared in .github/required-checks.yml for merge_group_fast
| Required check | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
sacpis
pushed a commit
to sacpis/cuda-quantum
that referenced
this pull request
Oct 30, 2026
# Let an application bring its own GPU dispatch kernel ## Summary The dispatcher's built-in unified kernel runs one block, and every thread of that block calls each handler. That suits most handlers, but not every application: some want a single thread, a cooperative grid, or clusters. This PR lets an application supply its own dispatch kernel. It reuses the run-time link introduced in NVIDIA#5722, so the custom kernel ends up in the same CUDA module as the bridge's hooks and the application's handlers. From the application's side, it takes three steps: write the kernel, add one CMake line, and make two calls at run time. ## Bringing your own dispatcher in three steps **1. Write the kernel.** It is a plain `.cu` file with one `extern "C" __global__` function. One thread drives the transport through the same three hooks the built-in kernel uses (`cudaq_dev_enter_kernel`, `cudaq_dev_rx_poll`, `cudaq_dev_tx_publish`), and the kernel decides how the handlers run. The framing helpers the built-in kernel uses (`unified_lookup_entry`, `write_response_header`) are shared in `unified_dispatch_device.cuh`. **2. Build it with one CMake call,** next to the binary that holds the handlers: ```cmake cudaq_realtime_add_dispatch_kernel(my_app my_dispatcher.cu) ``` The function comes with the `cudaq-realtime` package config. The kernel's file is not listed among the target's sources. **3. Look it up and hand the dispatcher a launcher,** after `cudaq_dispatcher_set_bridge`: ```cpp cudaKernel_t kernel; cudaq_dispatcher_get_device_kernel(dispatcher, "my_dispatcher", &kernel); cudaq_dispatcher_set_unified_launch(dispatcher, my_launcher, /*ctx=*/NULL); cudaq_dispatcher_start(dispatcher); ``` `cudaq_dispatcher_start` then calls `my_launcher` instead of launching the built-in kernel. The launcher receives everything the kernel needs (the bridge's transport context, the slot size, the function table, the shutdown flag, the stats counter and the stream) and launches the kernel any way it likes: any grid, any launch attributes. It launches on the given stream and returns `CUDAQ_OK`. That is all. The bridge, the handlers and the rest of the wiring don't change. ### Kernels and handlers: same lookup, different results `cudaq_dispatcher_get_device_kernel` is the twin of `cudaq_dispatcher_get_device_handler`: - **The same:** both find code from the application's own binary in the dispatcher's linked module. Whichever lookup comes first performs the link, and the handlers and the kernel of one dispatcher must come from the same binary. - **The difference:** a handler lookup returns a device address, which goes into the function table and is called from device code. CUDA has no way to find a device function by name, so the handler must be exported with `CUDAQ_REALTIME_EXPORT_DEVICE_FUNCTION`. A kernel lookup returns a host-side handle (`cudaKernel_t`) that the host uses to launch the kernel. A kernel is found by its own name, so it needs no export, and it never goes in the function table. ## Example: the test server `cudaq-realtime-test-server --path=device --dispatch=unified --dispatcher=single_thread_dispatcher` runs the whole flow, and is what the new two-process test shape uses. - **The kernel,** `unittests/dispatchers/single_thread_dispatcher.cu`, is about 40 lines and does the simplest possible thing. A single thread opens the transport session, then loops: wait for a request, look up its handler in the function table, call it, write the response header, publish. There are no block barriers and no shared-memory hand-off: the whole dispatch loop is visible at a glance. - **The build** adds it with one line in `unittests/test_server/CMakeLists.txt`: `cudaq_realtime_add_dispatch_kernel(cudaq-realtime-test-server .../single_thread_dispatcher.cu)`. - **The wiring** is step `[5k]` of the server: one call looks up the kernel, and one call installs the launcher. The launcher, `launch_own_dispatcher`, passes the kernel's arguments in order and launches one block of one thread through the driver API (`cuLaunchKernelEx`), which takes the handle on both CUDA 12 and 13. ## Under the hood: from a `.cu` file to a launchable kernel ```mermaid flowchart TB subgraph build ["Build time"] direction LR src["my_dispatcher.cu"] -->|"nvcc -fatbin -rdc, code=lto_80"| fatbin["LTO-IR fatbin"] fatbin -->|"asm .incbin"| section["app binary: __nv_relfatbin section, next to the handlers"] end subgraph runtime ["Run time"] direction LR link["nvJitLink -lto: built-in kernel + bridge hooks + app code"] --> lib["CUlibrary: one CUDA module"] lib -->|"cudaq_dispatcher_get_device_kernel: cuLibraryGetKernel"| handle["cudaKernel_t"] handle -->|"start: the app's launcher"| run["kernel running"] end build -->|"first lookup: cudaq_device_code_of_this_binary"| runtime ``` **Why it can't be an ordinary source.** The kernel calls the bridge's hooks, which only the bridge defines. If the kernel were a normal source of the application, the application's own device link would fail on those undefined hooks. Weak stubs don't help either: nvcc ignores `__attribute__((weak))` on device functions, so a stub would be linked in place of the bridge's hook. **What `cudaq_realtime_add_dispatch_kernel` does instead.** - It compiles the `.cu` file on its own into an LTO-IR fatbin, with the target's include directories, compile definitions and C++ standard. By default it targets the architecture of the built-in kernel; LTO-IR built for one architecture links for that GPU and every newer one. - It generates a small `.cpp` file that uses `.incbin` to place the fatbin in the binary's `__nv_relfatbin` section, next to the fatbins nvcc emits for the handlers. No host code registers it, so the binary's own device link never sees it. **At run time,** the first lookup reads the binary's whole `__nv_relfatbin` section with `cudaq_device_code_of_this_binary`. That section now holds the handlers and the custom kernel. As in NVIDIA#5722, the dispatcher links that code with its built-in kernel and the bridge's hooks into one module, with nvJitLink for the GPU it runs on, and loads it. Because everything is LTO-IR, the hooks are inlined into the custom kernel just as they are into the built-in one. `cudaq_dispatcher_get_device_kernel` then fetches the custom kernel from that module by name (`cuLibraryGetKernel`), and `start` hands control to the application's launcher. `start` still checks that every `DEVICE_CALL` entry holds an address from the linked module, but no longer checks `config.num_blocks`: the launcher chooses the grid. ## API changes - **New:** - `cudaq_dispatcher_get_device_kernel` (header-only, like `cudaq_dispatcher_get_device_handler`) and the exported `cudaq_dispatcher_get_device_kernel_in`. - The CMake function `cudaq_realtime_add_dispatch_kernel`, installed with the package config. - `unified_dispatch_device.cuh`, with the framing helpers shared by the built-in kernel and custom ones. - **Changed:** - `cudaq_dispatcher_set_unified_launch` also works over a bridge's GPU data plane. It takes a NULL transport context there, because the bridge's context is kept. - `cudaq_unified_launch_fn_t` now returns a `cudaq_status_t`, and a failure fails `cudaq_dispatcher_start`. A bridge launch override written against the old `void` form must add the return value. --------- Signed-off-by: Renaud Kauffmann <rkauffmann@nvidia.com>
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.
Summary
Until now the DEVICE + UNIFIED dispatch kernel was built for one transport at
compile time:
libcudaq-realtime-dispatch.acompiled the kernel together withthe transport's device hooks and every application device-linked that archive with its
handlers. A binary could only ever drive the one transport it was built
against.
This PR makes the unified kernel target-agnostic. The kernel now ships once,
as LTO-IR embedded in
libcudaq-realtime.so. At run time, the dispatcher linksthree pieces into one CUDA module with nvJitLink, for the GPU it runs on:
cudaq_dispatcher_set_bridgewired,DEVICE_CALLhandlers.A device data plane is now selected at run time, like any other bridge.
Changes by part
Application-facing API
include/cudaq/realtime/daemon/dispatcher/cudaq_realtime.hcudaq_dispatcher_get_device_handler, the one call an application makes for the device link: it returns the address of an exported handler in the dispatcher's linked module, and the first call performs the link. A header-onlystaticC function, so it runs in, and reads the device code of, the binary that holds the handler. It forwards to the exportedcudaq_dispatcher_get_device_handler_in. Removescudaq_launch_unified_dispatch_device. Documents the DEVICE + UNIFIED precedence ofset_bridge.include/cudaq/realtime/daemon/dispatcher/cudaq_status.h(new)cudaq_status_t, moved out ofcudaq_realtime.has a first step so that bridges do not need to depend on the dispatcher API. (More work is needed)include/cudaq/realtime/device_code/device_code.h(new)cudaq_device_image_t(relocatable device code: one fatbin, or several back to back as in a binary's__nv_relfatbinsection) andCUDAQ_REALTIME_EXPORT_DEVICE_FUNCTION, which exports a device function through a pointer global, since CUDA looks up kernels and globals by name but never device functions.include/cudaq/realtime/device_code/device_code_reader.h(new)cudaq_device_code_of_this_binary: a few lines of header-only C that return the calling binary's__nv_relfatbinsection as one image. It has to be compiled into each binary because the hidden__start___nv_relfatbinand__stop___nv_relfatbinsymbols resolve to the binary that references them.Bridge interface
include/cudaq/realtime/daemon/bridge/bridge_interface.hcudaq_gpu_dataplane_t { ctx, device_code, device_code_count }and aget_gpu_dataplanevtable entry, plus the publiccudaq_bridge_get_gpu_dataplane, mirroringcudaq_bridge_get_cpu_dataplane.include/cudaq/realtime/daemon/dispatcher/unified_device_transport.cuhcudaq_dev_enter_kernel,cudaq_dev_rx_poll,cudaq_dev_tx_publish, and the session storage size. The LINKAGE note now describes the run-time link.lib/daemon/bridge/bridge_interface_api.cppcudaq_bridge_get_gpu_dataplane:CUDAQ_ERR_UNSUPPORTEDfor a provider that leaves the entry NULL.lib/daemon/bridge/udp/bridge_impl.cpp,lib/daemon/bridge/cpu_roce/bridge_impl.cppget_gpu_dataplaneentry; these providers have no GPU data plane.Dispatcher
lib/daemon/dispatcher/cudaq_realtime_api.cppset_bridge, DEVICE + UNIFIED: a launch override (theUNIFIEDtransport context'slaunch_fn) takes precedence; otherwise the bridge's GPU data plane, whose images are copied; a bridge with neither is refused. Link:link_dispatcherlinks the embedded kernel, the bridge's images and the application's images, and finds the kernel by name. Lookup:cudaq_dispatcher_get_device_handler_inlinks on the first call, reuses the module afterwards, refuses device code from another binary (compared by content, so every translation unit of one binary works), and records each address it hands out.start: links the kernel and the hooks alone if no handler was looked up, refuses anyDEVICE_CALLentry whose address did not come from the lookup, and launches the linked kernel withcudaLaunchKernel.destroy: leaves the module loaded while a kernel may still run from it. The JIT state is grouped inJitDeviceLibrary.lib/daemon/dispatcher/unified_dispatch_kernel.cuextern "C"so it can be found by name in the linked module, and compiled to LTO-IR only. It has no transport dependency.lib/daemon/dispatcher/unified_transport_none.cu(deleted),lib/daemon/CMakeLists.txtlibcudaq-realtime-dispatch.aare gone.Run-time device link (
lib/device_code/, new)A static library,
libcudaq-realtime-device-code.a, linked privately intolibcudaq-realtime.so.CMakeLists.txtCUDAQ_REALTIME_DEVICE_CODE_MIN_ARCH(default 80). LTO-IR for one architecture links for that GPU and every newer one, and the kernel has no__CUDA_ARCH__branches. LinksCUDA::nvJitLinkPRIVATE.unified_dispatch_kernel_image.cppasm(".incbin ..."). It uses only generic GNU assembler directives, so it is portable across ELF targets (x86-64, AArch64).nvjitlink_linker.{h,cpp}link_device_code: splits each image into its fatbins, adds them to nvJitLink, links with-arch=sm_<this GPU> -lto, and loads the cubin withcudaLibraryLoadData. Failures print the nvJitLink version and its error log.lib/CMakeLists.txtgpu_roce bridge
lib/daemon/bridge/gpu_roce/bridge_impl.cppget_gpu_dataplanein unified mode: stages the DOCA transport context in device memory once per bridge, and hands over the library's device code. RefusesUNIFIED(no launch override) andRING_BUFFERin unified mode.lib/daemon/bridge/gpu_roce/gpu_roce_unified_transport.cu,gpu_roce_device_transport.cuh(new)gpu_roce_dev_*), status enum and session size, so it can move to hololink as is.lib/daemon/bridge/gpu_roce/gpu_roce_device_hooks.cu(new)cudaq_dev_*hooks: one-line forwarders to the transport, which map its status and check that its session fits the kernel's storage. Only the bridge sees the fixed hook names.lib/daemon/bridge/gpu_roce/CMakeLists.txt-rdcand one LTO-IR entry per architecture inCUDA_TARGET_ARCHS: DOCA picks its fences by__CUDA_ARCH__. With LTO-IR, the run-time link inlines transport, forwarder and kernel.include/cudaq/realtime/gpu_roce_bridge_common.hbridge_runno longer drives the unified kernel;--unifiedrefers togpu_roce_app.Tests and tools
unittests/bridge_interface/gpu_loopback/(new)gpu_loopback_bridge.cuis a test-only provider with a GPU data plane over mapped pinned memory, so the whole run-time link runs on any GPU without a NIC.test_gpu_dataplane.cppcovers: serving requests; the address and binary guards; relinking after a failed link; a provider image that cannot link; a provider without device code; the reader in a binary without-rdccode (no_device_code.cpp); and a binary with two fatbins whose second handler is looked up from its own translation unit (test_gpu_dataplane_second_handler.cu).unittests/c_api/(new)c_api_check.ccompiles the public headers and the header-only wrapper as C.check_no_nvjitlink.cmakefails if a public header includesnvJitLink.h.unittests/test_server/cudaq_realtime_test_server.cpp,unittests/bridge_interface/gpu_roce/gpu_roce_bridge.cppcudaq_dispatcher_get_device_handler.unittests/utils/rpc_increment_handler.{cu,cuh}(new),init_rpc_increment_function_table.cu,CMakeLists.txtunittests/utils/gpu_roce_test.sh--unifiedrunsgpu_roce_appand waits for its dispatcher to start.unittests/CMakeLists.txtscripts/install_devdeps.shlibnvjitlink-dev, needed to buildlibcudaq-realtimeonly.Documentation
realtime/docs/device_code_contract.md(new)realtime/docs/cudaq_realtime_host_api.md,docs/sphinx/using/realtime/host.mdcudaq_dispatcher_get_device_handlerand theset_bridgeshape table.realtime/docs/cudaq_realtime_network_interface.mdget_gpu_dataplane: how a provider serves the unified kernel.Requirements for application developers
-rdc=true, orCUDA_SEPARABLE_COMPILATION ON.CUDAQ_REALTIME_EXPORT_DEVICE_FUNCTION(name)(device_code.h). The handler itself may have internal linkage.cudaq_dispatcher_set_bridgewithcudaq_dispatcher_get_device_handler(dispatcher, "name", &fn), and put the returned address in theDEVICE_CALLentry. An address taken in the application's own module (for example by an init kernel) is refused bystart.start.__device__global set from the host lives in the application's module, not in the linked one.libnvJitLink.so.<major>, which ships with the CUDA runtime, at least as new as every toolkit that produced the linked code. Applications do not need the nvJitLink dev package.libcudaq-realtime-dispatch.a, or callingcudaq_launch_unified_dispatch_device.__CUDA_ARCH__should ship an entry per architecture it cares about.Requirements for bridge developers
unified_device_transport.cuh. The in-tree pattern (gpu_roce) keeps the transport's device code free of CUDA-Q headers, with its own names, and definescudaq_dev_*in the bridge as one-line forwarders. The session must fitCUDAQ_DEV_TRANSPORT_SESSION_BYTESandCUDAQ_DEV_TRANSPORT_SESSION_ALIGN.-rdc. Add LTO-IR so the run-time link inlines them into the kernel (--generate-code=arch=compute_XX,code=lto_XXper source, to avoid host LTO), with one entry per architecture if the code branches on__CUDA_ARCH__, as DOCA's headers do. Without LTO-IR the link still works, but the hooks stay real calls on every request.get_gpu_dataplane(interface version 4). Return a device-accessiblectxand the image fromcudaq_device_code_of_this_binary. The image storage must outlive the bridge, so keep it in the bridge's context.staticor an anonymous namespace), including those defined in headers: the link takes all of the binary's-rdccode, and two definitions of one name fail it.libcudaq-realtime.soloads the provider, never the reverse.get_gpu_dataplaneentry, NULL for a provider with no GPU data plane.UNIFIEDtransport context with alaunch_fn. Others refuse it, or answer with a NULLlaunch_fn, andset_bridgethen uses the GPU data plane.Breaking changes
cudaq_launch_unified_dispatch_deviceand the unified kernel inlibcudaq-realtime-dispatch.aare removed, together with theCUDAQ_REALTIME_UNIFIED_TRANSPORT_TUproperty and the null transport.DEVICE_CALLentries of a DEVICE + UNIFIED dispatcher must come fromcudaq_dispatcher_get_device_handler.cudaq_dispatcher_set_unified_launchnow requires a launch function.bridge_run --unifiedis removed; usegpu_roce_app --unified.Testing
cteston GB200, including the newGpuDataplaneTestsuite and the C compile check. The GPU data plane tests skip without a GPU.ctest -R TwoProcesson RoCE hardware (ConnectX, DOCA 3.4, Hololink 2.7), ring and unified shapes, for udp, cpu_roce and gpu_roce.gpu_roce_test.sh --unifiedon GB200 GPU 3 (on the NIC's NUMA node), 100 requests per run, 5 runs, all 100/100 matched. The average round trip is 5.94 µs (median of runs), the same as with the hooks compiled into the kernel (5.96 µs).