Skip to content

Commit 40e83bd

Browse files
authored
Add naive support for kernel-free and non-reduction collective operations (flagos-ai#299)
1 parent 33d451f commit 40e83bd

10 files changed

Lines changed: 1232 additions & 1053 deletions

File tree

flagcx/core/global_comm.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ struct flagcxComm {
4141
flagcxInnerComm_t host_comm;
4242
flagcxInnerComm_t homo_comm;
4343
flagcxHeteroComm_t hetero_comm;
44+
flagcxInnerComm_t homoInterComm;
45+
flagcxInnerComm_t tunerInnerComm; // innerComm selected by tuner
4446
// experimental for multi-nic support
4547
int homoInterRootRank;
4648
int homoInterMyRank;
4749
int homoInterRanks;
4850
std::vector<std::vector<int>> clusterInterRankList;
49-
flagcxInnerComm_t homoInterComm;
5051
std::vector<flagcxVendorType> clusterVendorMap;
5152
struct flagcxTuner *tuner;
5253
void *tunerContext;
5354
std::map<struct flagcxCommTag, flagcxInnerComm_t>
54-
homoCommMap; // key: commTag returned by tuner
55-
flagcxInnerComm_t tunerInnerComm; // innerComm selected by tuner
55+
homoCommMap; // key: commTag returned by tuner
5656
};
5757

5858
#endif // end include guard

flagcx/core/group.cc

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,14 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
196196
peer, comm->rank, op->args.p2pPeerSlotIdx,
197197
op->args.p2pPeerOpHash);
198198
}
199-
200199
// launch proxyRegister op if not yet registered
201-
flagcxConnector *peerConns[] = {
202-
comm->channels[op->channelId].peers[peer]->send};
203-
FLAGCXCHECK(flagcxNetRegisterBuffer(
204-
comm, p2p->buff, p2p->bytes, peerConns, 1, &op->args.regBufFlag,
205-
&op->args.regHandle));
200+
if (op->connection->transport == TRANSPORT_NET) {
201+
flagcxConnector *peerConns[] = {
202+
comm->channels[op->channelId].peers[peer]->send};
203+
FLAGCXCHECK(flagcxNetRegisterBuffer(
204+
comm, p2p->buff, p2p->bytes, peerConns, 1,
205+
&op->args.regBufFlag, &op->args.regHandle));
206+
}
206207
// we don't use semaphore tracking for device func for the moment
207208
if (deviceAsyncLoad && deviceAsyncStore) {
208209
FLAGCXCHECK(deviceAdaptor->eventCreate(&op->event,
@@ -227,10 +228,10 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
227228
} else {
228229
op->args.semaphore = semaphore;
229230
op->event = semaphore->getEvent();
231+
semaphore->counter++;
230232
FLAGCXCHECK(deviceAdaptor->eventRecord(op->event, op->stream));
231233
if (launchStream == nullptr) {
232234
launchStream = op->stream;
233-
semaphore->counter++;
234235
} else {
235236
FLAGCXCHECK(
236237
deviceAdaptor->streamWaitEvent(launchStream, op->event));
@@ -273,13 +274,14 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
273274
peer, comm->rank, op->args.p2pPeerSlotIdx,
274275
op->args.p2pPeerOpHash);
275276
}
276-
277277
// launch proxyRegister op if not yet registered
278-
flagcxConnector *peerConns[] = {
279-
comm->channels[op->channelId].peers[peer]->recv};
280-
FLAGCXCHECK(flagcxNetRegisterBuffer(
281-
comm, p2p->buff, p2p->bytes, peerConns, 1, &op->args.regBufFlag,
282-
&op->args.regHandle));
278+
if (op->connection->transport == TRANSPORT_NET) {
279+
flagcxConnector *peerConns[] = {
280+
comm->channels[op->channelId].peers[peer]->recv};
281+
FLAGCXCHECK(flagcxNetRegisterBuffer(
282+
comm, p2p->buff, p2p->bytes, peerConns, 1,
283+
&op->args.regBufFlag, &op->args.regHandle));
284+
}
283285
// we don't use semaphore tracking for device func for the moment
284286
if (deviceAsyncLoad && deviceAsyncStore) {
285287
std::vector<void *> argList;
@@ -304,10 +306,10 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
304306
} else {
305307
op->args.semaphore = semaphore;
306308
op->event = semaphore->getEvent();
309+
semaphore->counter++;
307310
FLAGCXCHECK(deviceAdaptor->eventRecord(op->event, op->stream));
308311
if (launchStream == nullptr) {
309312
launchStream = op->stream;
310-
semaphore->counter++;
311313
} else {
312314
FLAGCXCHECK(
313315
deviceAdaptor->streamWaitEvent(launchStream, op->event));
@@ -332,24 +334,35 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
332334
if (sendTasks[i]->bytes == recvTasks[j]->bytes &&
333335
sendTasks[i]->dtype == recvTasks[j]->dtype) {
334336
if (sendTasks[i]->buff != recvTasks[j]->buff) {
335-
flagcxEvent_t selfEvent = semaphore->getEvent();
336-
FLAGCXCHECK(deviceAdaptor->eventRecord(selfEvent,
337-
sendTasks[i]->stream));
337+
flagcxProxyOp *op;
338+
FLAGCXCHECK(flagcxCalloc(&op, 1));
339+
op->pattern = flagcxPatternSend;
340+
op->nbytes = sendTasks[i]->bytes;
341+
op->sendbuff = (uint8_t *)sendTasks[i]->buff;
342+
op->recvbuff = (uint8_t *)recvTasks[j]->buff;
343+
op->channelId = 0;
344+
op->root = peer;
345+
op->connection = comm->channels[op->channelId]
346+
.peers[peer]
347+
->send[0]
348+
.proxyConn.connection;
349+
op->stream = sendTasks[i]->stream;
350+
op->event = semaphore->getEvent();
351+
op->args.chunkSteps = 1; // single step
352+
op->args.semaphore = semaphore;
353+
semaphore->counter++;
354+
FLAGCXCHECK(
355+
deviceAdaptor->eventRecord(op->event, op->stream));
338356
if (launchStream == nullptr) {
339-
launchStream = sendTasks[i]->stream;
357+
launchStream = op->stream;
340358
} else {
341359
FLAGCXCHECK(deviceAdaptor->streamWaitEvent(launchStream,
342-
selfEvent));
360+
op->event));
343361
}
344-
345-
FLAGCXCHECK(deviceAdaptor->deviceMemcpy(
346-
recvTasks[j]->buff, sendTasks[i]->buff,
347-
sendTasks[i]->bytes, flagcxMemcpyDeviceToDevice,
348-
sendTasks[i]->stream, NULL));
362+
FLAGCXCHECK(flagcxProxySaveOp(comm, op));
349363
}
350364
free(sendTasks[i]);
351365
free(recvTasks[j]);
352-
353366
sendTasks.erase(sendTasks.begin() + i);
354367
recvTasks.erase(recvTasks.begin() + j);
355368
matched = true;
@@ -377,7 +390,6 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
377390
}
378391
}
379392
tasks->p2pOrderSteps = newOrderSteps;
380-
381393
comm = comm->groupNext;
382394
} while (comm != nullptr);
383395
}
@@ -409,8 +421,10 @@ static flagcxResult_t groupLaunch(struct flagcxAsyncJob *job_) {
409421
argsQueue.pop();
410422
}
411423
} else {
412-
FLAGCXCHECK(deviceAdaptor->launchHostFunc(launchStream, cpuAsyncKernel,
413-
(void *)semaphore.get()));
424+
if (launchStream != nullptr) {
425+
FLAGCXCHECK(deviceAdaptor->launchHostFunc(launchStream, cpuAsyncKernel,
426+
(void *)semaphore.get()));
427+
}
414428
}
415429
// deprecated code path for host func, since the previous
416430
// hang issue may be walked around by using zero copy

flagcx/core/p2p.cc

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ static std::map<int, std::pair<int, int>>
1111
void setP2pSlotInfo(int rank, int peerRank, size_t size, flagcxDataType_t dtype,
1212
int isRecv, int *opHash, size_t *slotIdx) {
1313
// TODO: try a better hash function to reduce collisions
14-
int key = rank * 1000 + int(size >> 12) + dtype * 100 + peerRank;
14+
int key = rank * 1000 + int(size >> 12) + dtype * 10 + peerRank * 100;
1515
int opHashCounter;
1616
auto it = p2pOpHashMap.find(key);
1717
if (it != p2pOpHashMap.end()) {
@@ -231,6 +231,51 @@ flagcxResult_t flagcxP2pProxyRecv(struct flagcxP2pResources *resources,
231231
return flagcxSuccess;
232232
}
233233

234+
flagcxResult_t flagcxP2pProxySelfCopy(struct flagcxP2pResources *resources,
235+
void *sendData, void *recvData,
236+
size_t size,
237+
struct flagcxProxyArgs *args) {
238+
// Make sure data is valid
239+
if (!args->semaphore->pollStart())
240+
return flagcxSuccess;
241+
242+
if (args->transmitted < args->chunkSteps) {
243+
// Perform single copy step
244+
if (args->copied < args->chunkSteps) {
245+
FLAGCXCHECK(deviceAdaptor->deviceMemcpy(
246+
recvData, sendData, size, flagcxMemcpyDeviceToDevice,
247+
resources->proxyInfo.stream, NULL));
248+
FLAGCXCHECK(
249+
deviceAdaptor->eventRecord(resources->proxyInfo.events[args->copied],
250+
resources->proxyInfo.stream));
251+
args->copied++;
252+
}
253+
254+
// Check for completed copy step
255+
if (args->transmitted < args->copied) {
256+
flagcxResult_t res = deviceAdaptor->eventQuery(
257+
resources->proxyInfo.events[args->transmitted]);
258+
if (res == flagcxSuccess) {
259+
args->transmitted++;
260+
}
261+
}
262+
} else {
263+
if (args->done != 1) {
264+
args->semaphore->signalCounter(1);
265+
// Deprecated device func handling
266+
if (deviceAsyncLoad && deviceAsyncStore) {
267+
if (args->deviceFuncRelaxedOrdering == 1) {
268+
FLAGCXCHECK(deviceAdaptor->deviceMemcpy(
269+
args->dlArgs, (void *)&args->hlArgs, sizeof(bool),
270+
flagcxMemcpyHostToDevice, resources->proxyInfo.stream, NULL));
271+
}
272+
}
273+
args->done = 1;
274+
}
275+
}
276+
return flagcxSuccess;
277+
}
278+
234279
flagcxResult_t flagcxP2pSendProxySetup(struct flagcxProxyConnection *connection,
235280
struct flagcxProxyState *proxyState,
236281
void *reqBuff, int reqSize,
@@ -249,8 +294,8 @@ flagcxResult_t flagcxP2pSendProxySetup(struct flagcxProxyConnection *connection,
249294

250295
// Allocate shared memory and store in resources->proxyInfo
251296
size_t shmSize = sizeof(struct flagcxP2pShm);
252-
INFO(FLAGCX_INIT,
253-
"flagcxP2pSendProxySetup: Allocating shared memory size=%zu", shmSize);
297+
INFO(FLAGCX_P2P, "flagcxP2pSendProxySetup: Allocating shared memory size=%zu",
298+
shmSize);
254299
FLAGCXCHECK(flagcxShmAllocateShareableBuffer(
255300
shmSize, &resources->proxyInfo.desc, (void **)&resources->proxyInfo.shm,
256301
NULL));
@@ -264,12 +309,12 @@ flagcxResult_t flagcxP2pSendProxySetup(struct flagcxProxyConnection *connection,
264309
resources->proxyInfo.shm->slots[i].peerDone = 1; // 1 = slot is free
265310
}
266311

267-
INFO(FLAGCX_INIT, "flagcxP2pSendProxySetup: Copying response, shm=%p",
312+
INFO(FLAGCX_P2P, "flagcxP2pSendProxySetup: Copying response, shm=%p",
268313
resources->proxyInfo.shm);
269314
memcpy(respBuff, &resources->proxyInfo, sizeof(struct flagcxP2pShmProxyInfo));
270315
*done = 1;
271316

272-
INFO(FLAGCX_INIT, "flagcxP2pSendProxySetup: Completed successfully");
317+
INFO(FLAGCX_P2P, "flagcxP2pSendProxySetup: Completed successfully");
273318
return flagcxSuccess;
274319
}
275320

@@ -278,7 +323,7 @@ flagcxResult_t flagcxP2pRecvProxySetup(struct flagcxProxyConnection *connection,
278323
void *reqBuff, int reqSize,
279324
void *respBuff, int respSize,
280325
int *done) {
281-
INFO(FLAGCX_INIT,
326+
INFO(FLAGCX_P2P,
282327
"flagcxP2pRecvProxySetup: reqSize=%d respSize=%d expectedReqSize=%zu "
283328
"expectedRespSize=%zu",
284329
reqSize, respSize, sizeof(struct flagcxP2pRequest),
@@ -334,7 +379,7 @@ flagcxP2pSendProxyConnect(struct flagcxProxyConnection *connection,
334379
}
335380

336381
*done = 1;
337-
INFO(FLAGCX_INIT, "flagcxP2pSendProxyConnect: Completed, recvFifo=%p",
382+
INFO(FLAGCX_P2P, "flagcxP2pSendProxyConnect: Completed, recvFifo=%p",
338383
resources->proxyInfo.recvFifo);
339384
return flagcxSuccess;
340385
}
@@ -361,7 +406,7 @@ flagcxP2pRecvProxyConnect(struct flagcxProxyConnection *connection,
361406
}
362407

363408
*done = 1;
364-
INFO(FLAGCX_INIT, "flagcxP2pRecvProxyConnect: Completed");
409+
INFO(FLAGCX_P2P, "flagcxP2pRecvProxyConnect: Completed");
365410
return flagcxSuccess;
366411
}
367412

flagcx/core/p2p.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ flagcxResult_t flagcxP2pProxyRecv(struct flagcxP2pResources *resources,
9292
void *data, size_t size,
9393
struct flagcxProxyArgs *args);
9494

95+
flagcxResult_t flagcxP2pProxySelfCopy(struct flagcxP2pResources *resources,
96+
void *sendData, void *recvData,
97+
size_t size,
98+
struct flagcxProxyArgs *args);
99+
95100
flagcxResult_t flagcxP2pSendProxySetup(struct flagcxProxyConnection *connection,
96101
struct flagcxProxyState *proxyState,
97102
void *reqBuff, int reqSize,

flagcx/core/proxy.cc

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,18 @@ flagcxResult_t flagcxProxySaveOp(struct flagcxHeteroComm *comm,
122122
*justInquire = false;
123123
switch (op->pattern) {
124124
case flagcxPatternSend:
125-
case flagcxPatternRecv: {
125+
// Self-copy will be saved as a send operation
126+
if (op->root == comm->rank)
127+
op->selfCopy = 1;
128+
FLAGCXCHECK(
129+
SaveProxy(comm, channel, proxySend, op->root, op, 0, justInquire));
130+
break;
131+
case flagcxPatternRecv:
126132
if (op->root == comm->rank)
127133
return flagcxSuccess;
128134
FLAGCXCHECK(
129-
SaveProxy(comm, channel,
130-
op->pattern == flagcxPatternSend ? proxySend : proxyRecv,
131-
op->root, op, 0, justInquire));
132-
} break;
135+
SaveProxy(comm, channel, proxyRecv, op->root, op, 0, justInquire));
136+
break;
133137
}
134138
return flagcxSuccess;
135139
}
@@ -207,8 +211,13 @@ static flagcxResult_t progressOps(struct flagcxProxyState *proxyState,
207211
} else if (op->connection->transport == TRANSPORT_P2P) {
208212
struct flagcxP2pResources *resources =
209213
(flagcxP2pResources *)op->connection->transportResources;
210-
flagcxP2pProxySend(resources, op->recvbuff, op->nbytes,
211-
&op->args);
214+
if (op->selfCopy == 0) {
215+
flagcxP2pProxySend(resources, op->recvbuff, op->nbytes,
216+
&op->args);
217+
} else {
218+
flagcxP2pProxySelfCopy(resources, op->sendbuff, op->recvbuff,
219+
op->nbytes, &op->args);
220+
}
212221
if (deviceAsyncLoad && deviceAsyncStore) {
213222
if (op->args.done == 1 && op->args.eventRecorded) {
214223
if (deviceAdaptor->eventQuery(op->event) == flagcxSuccess) {
@@ -407,8 +416,10 @@ static flagcxResult_t expectedProxyResponseStore(struct flagcxProxyState *state,
407416
return flagcxInternalError;
408417
}
409418

410-
memcpy(elem->respBuff, respBuff, respSize);
411-
free(respBuff);
419+
if (respSize > 0 && respBuff != NULL) {
420+
memcpy(elem->respBuff, respBuff, respSize);
421+
free(respBuff);
422+
}
412423
elem->done = true;
413424
elem->res = res;
414425
return flagcxSuccess;
@@ -731,7 +742,8 @@ static flagcxResult_t proxyProgressAsync(flagcxProxyAsyncOp **opHead,
731742
resources->netAdaptor->deregMr(resources->netRecvComm, handle));
732743
}
733744
done = 1;
734-
} else if (op->type == flagcxProxyMsgSetup) {
745+
} else if (op->type == flagcxProxyMsgSetup &&
746+
op->connection->transport == TRANSPORT_P2P) {
735747
if (op->connection->send) {
736748
// P2P Send side setup
737749
INFO(FLAGCX_PROXY, "Calling flagcxP2pSendProxySetup");
@@ -753,8 +765,11 @@ static flagcxResult_t proxyProgressAsync(flagcxProxyAsyncOp **opHead,
753765
"proxyProgressAsync opId=%p op.type=%d op.reqBuff=%p op.respSize=%d "
754766
"done",
755767
op->opId, op->type, op->reqBuff, op->respSize);
756-
if (op->type == flagcxProxyMsgConnect)
757-
__atomic_store_n(&op->connection->state, connConnected, __ATOMIC_RELEASE);
768+
if (op->connection->transport == TRANSPORT_NET) {
769+
if (op->type == flagcxProxyMsgConnect)
770+
__atomic_store_n(&op->connection->state, connConnected,
771+
__ATOMIC_RELEASE);
772+
}
758773

759774
/* if setup or connect is done, we should not return any error at this point
760775
* since flagcxSocketSend might already send the respBuff to the requester.

flagcx/core/proxy.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,12 @@ struct flagcxProxyArgs {
9696
size_t chunkSize;
9797
size_t stepSize;
9898
void *stepBuff;
99-
int waitCopy;
100-
int posted;
101-
int copied;
102-
int postFlush;
103-
int flushed;
104-
int transmitted;
99+
int waitCopy = 0;
100+
int posted = 0;
101+
int copied = 0;
102+
int postFlush = 0;
103+
int flushed = 0;
104+
int transmitted = 0;
105105
int sendStepMask;
106106
size_t totalCopySize;
107107
size_t totalPostSize;
@@ -186,6 +186,7 @@ struct flagcxProxyOp {
186186
flagcxProxyArgs args;
187187
flagcxStream_t stream;
188188
flagcxEvent_t event; // used to record host/device func
189+
int selfCopy = 0;
189190
};
190191

191192
#define FLAGCX_MAX_NETDEVS 128

0 commit comments

Comments
 (0)