CUDA-Q device_call Channels¶
cudaq::device_call lets a quantum kernel invoke a classical function on a
remote (or co-located) decoder/service while the program is running. The CUDA-Q
compiler lowers each call to the realtime RPC ABI; a device-call channel
carries the request to the service and the response back. This page describes
how channels are selected and documents the cpu_roce channel (a pure-CPU
RDMA transport). It complements the
CUDA-Q Realtime Host API and
Messaging Protocol documents, which describe the dispatcher and
the RPC wire format the channels carry.
The device_call model¶
A kernel calls a classical function through cudaq::device_call:
extern "C" int addThem(int a, int b);
__qpu__ int kernel(int a, int b) {
// 0 is the device id; addThem names the remote function.
return cudaq::device_call(0, addThem, a, b);
}
When compiled with realtime lowering (nvq++ -frealtime-lowering --enable-mlir),
the call is rewritten into the realtime
acquire/marshal/dispatch/release ABI. Two compile-time facts are baked in: the
device id (the first argument) and the function id
(fnv1a_hash("addThem")). The channel name is not compiled in.
Selecting a channel¶
The channel is chosen at runtime, on the application command line, and forwarded
into the runtime by cudaq::realtime::initialize(argc, argv):
./app --cudaq-device-call=<channel-name> [channel-specific args...]
The runtime parses --cudaq-device-call, instantiates the channel registered
under that name, and stores it in a session keyed by device id. At runtime,
cudaq::device_call(0, ...) resolves to the channel registered for device id 0.
The same compiled binary therefore runs over any channel by changing only the
launch flag.
Additional options:
--cudaq-device-call-slots=<N>: ring-buffer slot count--cudaq-device-call-slot-size=<bytes>: per-slot size--cudaq-device-call-timeout-ms=<ms>: per-dispatch timeout
The built-in channels (shared-memory, host-dispatch, gpu-dispatch) run an
in-process service and are not covered in depth here. host-dispatch uses the
libcudaq-realtime host path and can dispatch service entries as either
CUDAQ_DISPATCH_HOST_CALL handlers or CUDAQ_DISPATCH_GRAPH_LAUNCH workers.
The remainder of this page documents the cpu_roce channel.
Extending an in-process service¶
Realtime-enabled CUDA-Q installations expose the service extension interface
in <cudaq/realtime/device_call_service.h>. A plugin implements the abstract
DeviceCallService provider and returns one owned DeviceCallServiceSession
for the requested host or GPU dispatch mode. Returning null rejects an
unsupported mode.
The session owns its function table and any associated graphs, streams, or
mapped mailbox. Its dispatchTable() result must remain valid until CUDA-Q
calls stop() and destroys the session. CUDA-Q constructs the selected
DeviceCallChannel from that table, starts the service session after the
channel is ready, and stops and destroys the channel before stopping the
service session.
The service interface does not own or replace the transport. Ring allocation,
frame leasing, request publication, response completion, and transport
shutdown remain responsibilities of DeviceCallChannel.
Plugins export cudaqGetDeviceCallServicePluginInfo, which returns the
long-lived provider. Downstream CMake projects should link the exported
cudaq::cudaq-device-call-runtime target to receive the public header and
runtime implementation.
The cpu_roce channel¶
cpu_roce carries device_call RPCs over a pure-CPU RoCEv2 RDMA transport,
landing payloads directly in host memory at microsecond latency. It is an
external channel: unlike the in-process channels, its service runs in a
separate process (or a real FPGA) reached over the network. The transport is
CpuRoceTransceiver, described in CPU RoCE Transport.
Wire pattern (FPGA-compatible)¶
The channel is the caller and plays the FPGA role on the wire; the service plays the bridge/decoder role:
caller (channel) --IBV_WR_RDMA_WRITE_WITH_IMM--> service (request)
caller (channel) <--IBV_WR_SEND-- service (response)
This mirrors a real FPGA decode loop: the FPGA RDMA-Writes its syndromes into the decoder’s ring and receives the corrections as Sends. A real HSB-enabled FPGA could therefore replace the software channel unchanged.
Connection setup¶
Connected (UC) queue pairs require each end to learn the other’s QP number (and,
for the writer, the peer’s rkey) before traffic flows. The channel performs a
minimal bidirectional rendezvous between the transceiver’s setup() and
connect() phases. For a real FPGA service this exchange is driven by the HSB
control plane instead; only the rendezvous step differs, not the data plane. The
runtime pulls in no Hololink/HSB dependency.
Running it¶
cpu_roce requires a ConnectX NIC (or two RoCE ports / a loopback) and a
service process, so it is not run in CI. Provide the channel arguments as
bare key=value tokens on the same command line:
./app --cudaq-device-call=cpu_roce \
--cudaq-device-call-slots=64 --cudaq-device-call-slot-size=384 \
ib-device=<channel-ib-device> local-ip=<channel-ip> \
rendezvous-host=<service-ip> rendezvous-port=<port>
ib-device: the channel’s local IB devicelocal-ip: the channel’s RoCEIPv4rendezvous-host/rendezvous-port: where the service’s rendezvous server is listeningthe slot count/size must match the service’s ring geometry
Test harness¶
The repository ships a test-only service daemon and an orchestration script:
cpu_roce_test_daemonruns thelibcudaq-realtimehost dispatcher withCUDAQ_DISPATCH_HOST_CALLhandlers (keyed byfnv1a_hash) and a TCP rendezvous server. It stands in for a real decoder/bridge.cpu_roce_device_call_test.shconfigures the two RoCE ports and runs either:the default GoogleTest fixture (
CpuRoceDispatchTest), which drives the channel ABI directly, or--appmode, whichnvq++-compiles a real__qpu__device_callapplication, spawns the daemon, runs the app overcpu_roce, and verifies the compiler-lowered result.
# Compiler-driven proof over a two-port loopback:
cpu_roce_device_call_test.sh \
--channel-device <ibdevA> --channel-ip 10.0.0.1 \
--daemon-device <ibdevB> --daemon-ip 10.0.0.2 \
--setup-network --app