Skip to content

Commit 1bbf457

Browse files
committed
fix(device-ir): pre-allocate net contexts in DevComm, expand scalar IR tests
1 parent e5e538a commit 1bbf457

8 files changed

Lines changed: 698 additions & 18 deletions

File tree

bindings/ir/flagcx_device_scalar_ir_impl.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
/*************************************************************************
22
* Copyright (c) 2026 BAAI. All rights reserved.
33
*
4-
* FlagCX Device Scalar IR API — Implementation (Step 2: Coop + Team).
4+
* FlagCX Device Scalar IR API — Implementation.
55
*
6-
* This file implements the scalar cooperative group and team functions.
6+
* This file implements the scalar (S-suffixed) IR functions.
77
* It is included by the bitcode compilation unit alongside the existing
88
* flagcx_device_wrapper_impl.h.
9-
*
10-
* Remaining categories (barrier, net) will be added in subsequent steps.
119
************************************************************************/
1210
#ifndef FLAGCX_DEVICE_SCALAR_IR_IMPL_H_
1311
#define FLAGCX_DEVICE_SCALAR_IR_IMPL_H_
@@ -323,20 +321,24 @@ flagcxWorldBarrierSyncS(const void *netOpaque, flagcxCoopKind_t coopKind,
323321
/* ================================================================
324322
* Category 9: Net — Obtain Pre-Allocated Transport (1)
325323
*
326-
* Constructs a flagcxDevNet on the stack and returns a pointer.
327-
* NOTE: This returns a stack pointer — the caller must use it
328-
* immediately within the same kernel invocation scope. The returned
329-
* pointer is valid for the duration of the calling function.
324+
* Returns a pointer into the comm-owned pre-allocated flagcxDevNet[]
325+
* array (device-resident, built by flagcxDevCommGetDevicePtr).
326+
* Lifetime: valid as long as the DevComm device pointer is alive
327+
* (freed on flagcxDevCommDestroy or flagcxDevCommFreeDevicePtr).
328+
* The returned pointer is read-only and safe to use from any thread.
330329
* ================================================================ */
331330

332331
FLAGCX_IR_EXTERN_C FLAGCX_DEVICE_INLINE_DECORATOR const void *
333332
flagcxDevNetGetFromCommS(const void *commOpaque, int idx) {
334333
const flagcxDevComm *comm = (const flagcxDevComm *)commOpaque;
335-
// When always-inlined, the static local is promoted into the caller's
336-
// scope by LLVM. This provides stable storage for the returned pointer.
337-
static flagcxDevNet storage;
338-
::new (&storage) flagcxDevNet(*comm, idx);
339-
return &storage;
334+
// Return pointer into pre-allocated device array (built by
335+
// flagcxDevCommGetDevicePtr). Each entry is a fully-constructed
336+
// flagcxDevNet for that context index — no per-call construction.
337+
const flagcxDevNet *nets = (const flagcxDevNet *)comm->_netContexts;
338+
if (!nets || comm->_contextCount <= 0)
339+
return (const void *)0;
340+
int safeIdx = (int)((unsigned)idx % (unsigned)comm->_contextCount);
341+
return &nets[safeIdx];
340342
}
341343

342344
/* ================================================================

flagcx/adaptor/flagcx_device.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
#include <new>
2626
#include <sched.h> // sched_yield
2727

28+
// Host-visible helpers implemented in device_api_host_helpers.cu (compiled by
29+
// nvcc)
30+
extern "C" size_t flagcxDevNetSizeOf();
31+
extern "C" void flagcxDevNetConstructArray(void *dst, const void *comm,
32+
int count);
33+
2834
// ==========================================================================
2935
// Shared: IPC peer pointer exchange (used by both tiers)
3036
// ==========================================================================
@@ -1056,6 +1062,9 @@ extern "C" flagcxResult_t flagcxDevCommDestroy(flagcxComm_t comm,
10561062
}
10571063

10581064
// Device pointer cache cleanup
1065+
if (devComm->cachedNetContextsPtr) {
1066+
flagcxCommDeferFree(comm, devComm->cachedNetContextsPtr, flagcxMemDevice);
1067+
}
10591068
if (devComm->cachedDevicePtr) {
10601069
flagcxCommDeferFree(comm, devComm->cachedDevicePtr, flagcxMemDevice);
10611070
}
@@ -1268,6 +1277,32 @@ extern "C" flagcxResult_t flagcxDevCommGetDevicePtr(flagcxDevComm_t devComm,
12681277
// Construct value struct on host stack
12691278
flagcxDevComm hostCopy(*devComm);
12701279

1280+
// Pre-allocate net contexts (one flagcxDevNet per context) in device memory
1281+
void *netDevPtr = nullptr;
1282+
if (hostCopy._contextCount > 0) {
1283+
size_t netArraySize = hostCopy._contextCount * flagcxDevNetSizeOf();
1284+
// Build array on host heap
1285+
void *hostNets = malloc(netArraySize);
1286+
if (hostNets) {
1287+
flagcxDevNetConstructArray(hostNets, &hostCopy, hostCopy._contextCount);
1288+
// Copy to device
1289+
flagcxResult_t netRes = deviceAdaptor->deviceMalloc(
1290+
&netDevPtr, netArraySize, flagcxMemDevice, NULL);
1291+
if (netRes == flagcxSuccess) {
1292+
netRes =
1293+
deviceAdaptor->deviceMemcpy(netDevPtr, hostNets, netArraySize,
1294+
flagcxMemcpyHostToDevice, NULL, NULL);
1295+
}
1296+
if (netRes != flagcxSuccess) {
1297+
if (netDevPtr)
1298+
deviceAdaptor->deviceFree(netDevPtr, flagcxMemDevice, NULL);
1299+
netDevPtr = nullptr;
1300+
}
1301+
free(hostNets);
1302+
}
1303+
hostCopy._netContexts = netDevPtr;
1304+
}
1305+
12711306
// Allocate device memory and copy
12721307
void *dPtr = nullptr;
12731308
flagcxResult_t res = flagcxSuccess;
@@ -1280,12 +1315,16 @@ extern "C" flagcxResult_t flagcxDevCommGetDevicePtr(flagcxDevComm_t devComm,
12801315
res, fail);
12811316

12821317
devComm->cachedDevicePtr = dPtr;
1318+
devComm->cachedNetContextsPtr = netDevPtr;
12831319
*devPtr = dPtr;
12841320
pthread_mutex_unlock(&devComm->cachedPtrMutex);
12851321
return flagcxSuccess;
12861322

12871323
fail:
12881324
pthread_mutex_unlock(&devComm->cachedPtrMutex);
1325+
if (netDevPtr) {
1326+
deviceAdaptor->deviceFree(netDevPtr, flagcxMemDevice, NULL);
1327+
}
12891328
if (dPtr) {
12901329
deviceAdaptor->deviceFree(dPtr, flagcxMemDevice, NULL);
12911330
}
@@ -1298,9 +1337,14 @@ extern "C" flagcxResult_t flagcxDevCommFreeDevicePtr(flagcxDevComm_t devComm) {
12981337

12991338
pthread_mutex_lock(&devComm->cachedPtrMutex);
13001339
void *ptr = devComm->cachedDevicePtr;
1340+
void *netPtr = devComm->cachedNetContextsPtr;
13011341
devComm->cachedDevicePtr = nullptr;
1342+
devComm->cachedNetContextsPtr = nullptr;
13021343
pthread_mutex_unlock(&devComm->cachedPtrMutex);
13031344

1345+
if (netPtr) {
1346+
FLAGCXCHECK(deviceAdaptor->deviceFree(netPtr, flagcxMemDevice, NULL));
1347+
}
13041348
if (ptr) {
13051349
FLAGCXCHECK(deviceAdaptor->deviceFree(ptr, flagcxMemDevice, NULL));
13061350
}

flagcx/adaptor/include/device_api/flagcx_device_core.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,20 @@ struct flagcxDevComm {
7171
int _contextCount;
7272
int _nInterPeers;
7373

74+
// Pre-allocated net contexts (one per _contextCount, device memory).
75+
// Set by flagcxDevCommGetDevicePtr; nullptr when contextCount == 0.
76+
// Actually flagcxDevNet[] but kept as void* for C/opaque linkage.
77+
void *_netContexts;
78+
7479
FLAGCX_HOST_DEVICE_INLINE flagcxDevComm()
7580
: _commBase(), _signalCount(0), _counterCount(0), _contextCount(0),
76-
_nInterPeers(0) {}
81+
_nInterPeers(0), _netContexts(nullptr) {}
7782

7883
#ifndef __clang_llvm_bitcode_lib__
7984
FLAGCX_HOST_DEVICE_INLINE flagcxDevComm(const flagcxDevCommInternal &di)
8085
: _signalCount(di.signalCount), _counterCount(di.counterCount),
81-
_contextCount(di.contextCount), _nInterPeers(di.nInterPeers) {
86+
_contextCount(di.contextCount), _nInterPeers(di.nInterPeers),
87+
_netContexts(nullptr) {
8288
if (di.devComm) {
8389
_commBase = *(typename DeviceAPI::Comm *)di.devComm;
8490
} else {

flagcx/adaptor/include/device_api/flagcx_device_internal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ struct flagcxDevCommInternal {
9999
flagcxInnerDevComm_t devComm; // Typed vendor handle (per-adaptor struct)
100100

101101
// ---- Device pointer cache (for Triton integration) ----
102-
void *cachedDevicePtr; // Lazily allocated by flagcxDevCommGetDevicePtr
103-
pthread_mutex_t cachedPtrMutex; // Protects lazy init of cachedDevicePtr
102+
void *cachedDevicePtr; // Lazily allocated by flagcxDevCommGetDevicePtr
103+
void *cachedNetContextsPtr; // Device memory for pre-allocated flagcxDevNet[]
104+
pthread_mutex_t cachedPtrMutex; // Protects lazy init of cachedDevicePtr and
105+
// cachedNetContextsPtr
104106
};
105107

106108
// ============================================================
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*************************************************************************
2+
* Copyright (c) 2026 BAAI. All rights reserved.
3+
*
4+
* Host-callable helpers that require device-compiler visibility of
5+
* device-only types (e.g. flagcxDevNet). Compiled by nvcc/hipcc,
6+
* linked into libflagcx.so, called from flagcx_device.cc via extern "C".
7+
************************************************************************/
8+
9+
#include "device_api/flagcx_device_core.h"
10+
#include <new>
11+
12+
extern "C" size_t flagcxDevNetSizeOf() { return sizeof(flagcxDevNet); }
13+
14+
extern "C" void flagcxDevNetConstructArray(void *dst, const void *comm,
15+
int count) {
16+
const flagcxDevComm *devComm = (const flagcxDevComm *)comm;
17+
flagcxDevNet *nets = (flagcxDevNet *)dst;
18+
for (int i = 0; i < count; i++) {
19+
::new (&nets[i]) flagcxDevNet(*devComm, i);
20+
}
21+
}

0 commit comments

Comments
 (0)