Skip to content

Commit c42372f

Browse files
committed
Route every decoder device pin through one resolver and two sanctioned wrappers
Signed-off-by: Melody Ren <melodyr@nvidia.com>
1 parent 890e0d7 commit c42372f

5 files changed

Lines changed: 44 additions & 29 deletions

File tree

libs/qec/lib/hardware_guards.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
#pragma once
1010

11+
#include "cudaq/qec/decoder.h"
12+
13+
#include <cassert>
1114
#include <cuda_runtime_api.h>
1215
#include <stdexcept>
1316
#include <string>
@@ -30,6 +33,37 @@ inline void set_cuda_device_for_decode(int target) {
3033
") failed: " + cudaGetErrorString(err));
3134
}
3235

36+
/// Resolve a decoder's device: its cuda_device_id, or 0 when unpinned. For
37+
/// paths that need a concrete device (graph capture/launch), unlike
38+
/// set_cuda_device_for_decode() which no-ops on < 0.
39+
inline int decode_device_for(int cuda_device_id) {
40+
return cuda_device_id >= 0 ? cuda_device_id : 0;
41+
}
42+
43+
/// Pin before dispatch / decode / get_corrections / reset. No-op for an
44+
/// unpinned decoder (cuda_device_id < 0): a CPU decoder must not be forced onto
45+
/// a GPU. The sanctioned dispatch pin for every transport.
46+
inline void pin_decode_device(const cudaq::qec::decoder &dec) {
47+
set_cuda_device_for_decode(dec.get_cuda_device_id());
48+
}
49+
50+
/// Capture a decoder's realtime graph, pinned to its device so capture lands on
51+
/// the GPU every launch uses. Unpinned resolves to device 0 (a graph needs a
52+
/// concrete device). The only sanctioned caller of capture_decode_graph().
53+
inline void *capture_graph_pinned(cudaq::qec::decoder &dec,
54+
int reserved_sms = 0) {
55+
const int device = decode_device_for(dec.get_cuda_device_id());
56+
set_cuda_device_for_decode(device);
57+
void *raw = dec.capture_decode_graph(reserved_sms);
58+
#ifndef NDEBUG
59+
int current = -1;
60+
(void)cudaGetDevice(&current);
61+
assert(current == device &&
62+
"capture_graph_pinned: capture did not land on the decoder's device");
63+
#endif
64+
return raw;
65+
}
66+
3367
/// RAII: set the calling thread's CUDA device, restore the previous device on
3468
/// scope exit. No-op for target < 0. Lib-private and header-only so decoder
3569
/// plugins built as separate .so files can reuse it (PR2 extends this header

libs/qec/lib/realtime/decoding-server-cqr/DecodingServer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "DecodingServer.h"
1010
#include "CpuRoceTransceiver.h"
11+
#include "../../hardware_guards.h"
1112

1213
#include "cudaq/qec/logger.h"
1314
#include "cudaq/qec/realtime/decoding_config.h"
@@ -45,7 +46,7 @@ using cudaq::qec::decoding::config::DecoderTransport;
4546
/// graphs cannot split capture and launch across devices, so the decoder must
4647
/// be pinned to that device.
4748
int resolve_decode_device(int decoder_pin) {
48-
return decoder_pin >= 0 ? decoder_pin : 0;
49+
return detail_affinity::decode_device_for(decoder_pin);
4950
}
5051

5152
std::unique_ptr<ITransceiver>

libs/qec/lib/realtime/decoding-server-cqr/DecodingSession.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
******************************************************************************/
88

99
#include "DecodingSession.h"
10-
#include "DecodingServer.h"
1110
#include "RpcWireFormat.h"
1211
#include "../../hardware_guards.h"
1312
#include "cudaq/qec/logger.h"
@@ -20,19 +19,6 @@
2019

2120
namespace cudaq::qec::decoding_server {
2221

23-
namespace {
24-
25-
void set_graph_capture_device(const cudaq::qec::decoder &decoder) {
26-
const int device = resolve_decode_device(decoder.get_cuda_device_id());
27-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(device);
28-
if (device >= 0)
29-
CUDA_QEC_INFO(
30-
"DecodingSession::create: set CUDA device {} before graph capture",
31-
device);
32-
}
33-
34-
} // namespace
35-
3622
// Busy high-water mark across all sessions (worker threads increment while
3723
// executing an item).
3824
static std::atomic<uint64_t> g_busy_sessions{0};
@@ -67,8 +53,7 @@ DecodingSession::create(std::unique_ptr<cudaq::qec::decoder> decoder,
6753
s->dec = std::move(decoder);
6854

6955
if (s->dec->supports_graph_dispatch()) {
70-
set_graph_capture_device(*s->dec);
71-
void *gr = s->dec->capture_decode_graph();
56+
void *gr = cudaq::qec::detail_affinity::capture_graph_pinned(*s->dec);
7257
s->graph_resources =
7358
GraphResourcesPtr(gr, GraphResourcesDeleter{s->dec.get()});
7459
}
@@ -86,8 +71,7 @@ void DecodingSession::start_worker() {
8671
auto pin_result = pinned.get_future();
8772
worker = std::thread([this, &pinned] {
8873
try {
89-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(
90-
dec->get_cuda_device_id());
74+
cudaq::qec::detail_affinity::pin_decode_device(*dec);
9175
pinned.set_value();
9276
} catch (...) {
9377
pinned.set_exception(std::current_exception());

libs/qec/lib/realtime/qec_realtime_session.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,7 @@ static void apply_decoder_cuda_device(cudaq::qec::decoder *dec) {
140140
// Throws on failure (fail fast): host dispatch surfaces it as an error
141141
// response and graph initialization aborts, rather than continuing on
142142
// whichever device happened to be current.
143-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(
144-
dec->get_cuda_device_id());
143+
cudaq::qec::detail_affinity::pin_decode_device(*dec);
145144
}
146145

147146
// Two-ring response writer: the request stays in `rx_slot` (read-only); the
@@ -557,9 +556,9 @@ void qec_realtime_session::capture_decoder_graphs() {
557556
"qec_realtime_session::initialize: decoder " + std::to_string(i) +
558557
" does not support graph dispatch in DEVICE mode.");
559558

560-
apply_decoder_cuda_device(dec);
561559
// reserved_sms = 0 is intentional for the inproc_rpc desktop / CI path.
562-
void *raw = dec->capture_decode_graph(/*reserved_sms=*/0);
560+
void *raw =
561+
cudaq::qec::detail_affinity::capture_graph_pinned(*dec, /*sms=*/0);
563562
if (!raw)
564563
throw std::runtime_error("qec_realtime_session::initialize: decoder " +
565564
std::to_string(i) +

libs/qec/lib/realtime/realtime_decoding.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,7 @@ void enqueue_syndromes(std::size_t decoder_id, uint8_t *syndromes,
432432
// decoder's pinned device before decoding (set-if-different; throws on
433433
// failure) -- and before the capture callback, so a pin failure cannot
434434
// record a round that was never decoded.
435-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(
436-
decoder->get_cuda_device_id());
435+
cudaq::qec::detail_affinity::pin_decode_device(*decoder);
437436
capture_syndromes();
438437

439438
std::vector<uint8_t> syndrome_u8(syndrome_length);
@@ -501,8 +500,7 @@ void get_corrections(std::size_t decoder_id, uint8_t *corrections,
501500
#endif
502501

503502
// clear_corrections may touch device memory in some plugins.
504-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(
505-
decoder->get_cuda_device_id());
503+
cudaq::qec::detail_affinity::pin_decode_device(*decoder);
506504
auto ret = decoder->get_obs_corrections();
507505
for (std::size_t i = 0; i < correction_length; ++i) {
508506
corrections[i] = ret[i];
@@ -538,8 +536,7 @@ void reset_decoder(std::size_t decoder_id) {
538536
}
539537
#endif
540538

541-
cudaq::qec::detail_affinity::set_cuda_device_for_decode(
542-
decoder->get_cuda_device_id());
539+
cudaq::qec::detail_affinity::pin_decode_device(*decoder);
543540
decoder->reset_decoder();
544541
}
545542

0 commit comments

Comments
 (0)