1use std::ffi::c_void;
21
22pub type NcclComm = *mut c_void;
24
25#[repr(C)]
27#[derive(Copy, Clone)]
28pub struct NcclUniqueId {
29 pub internal: [u8; 128],
30}
31
32#[repr(C)]
35pub struct NcclConfig {
36 pub size: usize,
38 pub magic: u32,
40 pub version: u32,
42 pub blocking: i32,
44 pub cga_cluster_size: i32,
46 pub min_ctas: i32,
48 pub max_ctas: i32,
50 pub net_name: [u8; 8],
52 pub split_share: i32,
54}
55
56impl NcclConfig {
57 pub fn non_blocking() -> Self {
62 Self {
64 size: std::mem::size_of::<Self>(),
65 magic: 0x4e43434c, version: 22907, blocking: 0, cga_cluster_size: -1, min_ctas: -1,
70 max_ctas: -1,
71 net_name: [0; 8],
72 split_share: -1,
73 }
74 }
75}
76
77#[repr(C)]
79#[derive(Debug, Copy, Clone, PartialEq, Eq)]
80pub enum NcclResult {
81 Success = 0,
82 UnhandledCudaError = 1,
83 SystemError = 2,
84 InternalError = 3,
85 InvalidArgument = 4,
86 InvalidUsage = 5,
87 RemoteError = 6,
88 InProgress = 7,
89}
90
91#[repr(C)]
93#[derive(Debug, Copy, Clone)]
94#[allow(dead_code)]
95pub enum NcclDataType {
96 Int8 = 0,
97 Uint8 = 1,
98 Int32 = 2,
99 Uint32 = 3,
100 Int64 = 4,
101 Uint64 = 5,
102 Float16 = 6,
103 Float32 = 7,
104 Float64 = 8,
105 Bfloat16 = 9,
106}
107
108#[repr(C)]
110#[derive(Debug, Copy, Clone)]
111#[allow(dead_code)]
112pub enum NcclRedOp {
113 Sum = 0,
114 Prod = 1,
115 Max = 2,
116 Min = 3,
117 Avg = 4,
118}
119
120#[link(name = "nccl")]
121unsafe extern "C" {
122 pub fn ncclGetUniqueId(id: *mut NcclUniqueId) -> NcclResult;
123
124 pub fn ncclCommInitRank(
125 comm: *mut NcclComm,
126 nranks: i32,
127 id: NcclUniqueId,
128 rank: i32,
129 ) -> NcclResult;
130
131 pub fn ncclCommInitRankConfig(
134 comm: *mut NcclComm,
135 nranks: i32,
136 id: NcclUniqueId,
137 rank: i32,
138 config: *const NcclConfig,
139 ) -> NcclResult;
140
141 pub fn ncclAllReduce(
142 sendbuf: *const c_void,
143 recvbuf: *mut c_void,
144 count: usize,
145 datatype: NcclDataType,
146 op: NcclRedOp,
147 comm: NcclComm,
148 stream: u64, ) -> NcclResult;
150
151 pub fn ncclBroadcast(
152 sendbuf: *const c_void,
153 recvbuf: *mut c_void,
154 count: usize,
155 datatype: NcclDataType,
156 root: i32,
157 comm: NcclComm,
158 stream: u64,
159 ) -> NcclResult;
160
161 pub fn ncclCommDestroy(comm: NcclComm) -> NcclResult;
162
163 pub fn ncclGetErrorString(result: NcclResult) -> *const std::ffi::c_char;
164
165 pub fn ncclCommRegister(
167 comm: NcclComm,
168 buff: *mut c_void,
169 size: usize,
170 handle: *mut *mut c_void,
171 ) -> NcclResult;
172
173 pub fn ncclCommDeregister(comm: NcclComm, handle: *mut c_void) -> NcclResult;
174
175 pub fn ncclMemAlloc(ptr: *mut *mut c_void, size: usize) -> NcclResult;
192
193 pub fn ncclMemFree(ptr: *mut c_void) -> NcclResult;
194
195 pub fn ncclSend(
197 sendbuf: *const c_void,
198 count: usize,
199 datatype: NcclDataType,
200 peer: i32,
201 comm: NcclComm,
202 stream: u64,
203 ) -> NcclResult;
204
205 pub fn ncclRecv(
206 recvbuf: *mut c_void,
207 count: usize,
208 datatype: NcclDataType,
209 peer: i32,
210 comm: NcclComm,
211 stream: u64,
212 ) -> NcclResult;
213
214 pub fn ncclAllGather(
216 sendbuf: *const c_void,
217 recvbuf: *mut c_void,
218 sendcount: usize,
219 datatype: NcclDataType,
220 comm: NcclComm,
221 stream: u64,
222 ) -> NcclResult;
223
224 pub fn ncclReduceScatter(
226 sendbuf: *const c_void,
227 recvbuf: *mut c_void,
228 recvcount: usize,
229 datatype: NcclDataType,
230 op: NcclRedOp,
231 comm: NcclComm,
232 stream: u64,
233 ) -> NcclResult;
234
235 pub fn ncclGroupStart() -> NcclResult;
237 pub fn ncclGroupEnd() -> NcclResult;
238
239 pub fn ncclCommGetAsyncError(comm: NcclComm, async_error: *mut NcclResult) -> NcclResult;
241
242 pub fn ncclCommAbort(comm: NcclComm) -> NcclResult;
245}
246
247#[link(name = "cuda")]
250unsafe extern "C" {
251 fn cuStreamCreate(phStream: *mut u64, flags: u32) -> i32;
252 fn cuEventCreate(phEvent: *mut u64, flags: u32) -> i32;
253 fn cuEventRecord(hEvent: u64, hStream: u64) -> i32;
254 fn cuStreamWaitEvent(hStream: u64, hEvent: u64, flags: u32) -> i32;
255 fn cuEventDestroy_v2(hEvent: u64) -> i32;
256 fn cuStreamDestroy_v2(hStream: u64) -> i32;
257 fn cuStreamSynchronize(hStream: u64) -> i32;
258}
259
260pub fn create_stream() -> anyhow::Result<u64> {
261 let mut stream: u64 = 0;
262 let status = unsafe { cuStreamCreate(&mut stream, 1) }; if status != 0 {
264 anyhow::bail!("cuStreamCreate failed: status {status}");
265 }
266 Ok(stream)
267}
268
269pub fn create_event() -> anyhow::Result<u64> {
270 let mut event: u64 = 0;
271 let status = unsafe { cuEventCreate(&mut event, 0x02) }; if status != 0 {
273 anyhow::bail!("cuEventCreate failed: status {status}");
274 }
275 Ok(event)
276}
277
278pub fn record_event(event: u64, stream: u64) -> anyhow::Result<()> {
279 let status = unsafe { cuEventRecord(event, stream) };
280 if status != 0 {
281 anyhow::bail!("cuEventRecord failed: status {status}");
282 }
283 Ok(())
284}
285
286pub fn stream_wait_event(stream: u64, event: u64) -> anyhow::Result<()> {
287 let status = unsafe { cuStreamWaitEvent(stream, event, 0) };
288 if status != 0 {
289 anyhow::bail!("cuStreamWaitEvent failed: status {status}");
290 }
291 Ok(())
292}
293
294pub fn destroy_event(event: u64) {
295 if event != 0 {
296 unsafe { cuEventDestroy_v2(event) };
297 }
298}
299
300pub fn destroy_stream(stream: u64) {
301 if stream != 0 {
302 unsafe { cuStreamDestroy_v2(stream) };
303 }
304}
305
306pub fn sync_stream(stream: u64) -> anyhow::Result<()> {
307 let status = unsafe { cuStreamSynchronize(stream) };
308 if status != 0 {
309 anyhow::bail!("cuStreamSynchronize failed: status {status}");
310 }
311 Ok(())
312}
313
314pub unsafe fn nccl_mem_alloc(size: usize) -> anyhow::Result<*mut c_void> {
327 let mut ptr: *mut c_void = std::ptr::null_mut();
328 let result = unsafe { ncclMemAlloc(&mut ptr, size) };
329 check_nccl(result, "ncclMemAlloc")?;
330 Ok(ptr)
331}
332
333pub unsafe fn nccl_mem_free(ptr: *mut c_void) -> anyhow::Result<()> {
338 let result = unsafe { ncclMemFree(ptr) };
339 check_nccl(result, "ncclMemFree")
340}
341
342pub fn check_nccl(result: NcclResult, context: &str) -> anyhow::Result<()> {
344 if result == NcclResult::Success {
345 Ok(())
346 } else {
347 let msg = unsafe {
348 let ptr = ncclGetErrorString(result);
349 if ptr.is_null() {
350 format!("{result:?}")
351 } else {
352 std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned()
353 }
354 };
355 anyhow::bail!("NCCL error in {context}: {msg} ({result:?})")
356 }
357}