|
| 1 | +/** |
| 2 | + * Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| 3 | + * |
| 4 | + * See file LICENSE for terms. |
| 5 | + */ |
| 6 | + |
| 7 | +/* |
| 8 | + * Push-based alltoall with one CPU SHM barrier. |
| 9 | + * |
| 10 | + * Each rank pushes its chunks directly to peer destination buffers. Once all |
| 11 | + * pushes complete (detected via cudaEventQuery on the rank's own stream), the |
| 12 | + * rank enters a single CPU SHM barrier. When all ranks have arrived, every |
| 13 | + * rbuf is fully populated and UCC_OK can be returned. |
| 14 | + * |
| 15 | + * This replaces the pull-based CE algorithm's TWO barriers with ONE: |
| 16 | + * - No SETUP barrier: we push from our own sbuf so no cudaStreamWaitEvent on |
| 17 | + * peer events is needed, eliminating the stale-event problem entirely. |
| 18 | + * - ONE FINAL barrier: ensures all peers have completed their pushes into our |
| 19 | + * rbuf before we signal completion to the user. |
| 20 | + * |
| 21 | + * Counts and displacements are uniform, so the destination offset is |
| 22 | + * rank * chunk and no SETUP exchange is required (needs_setup == 0). The |
| 23 | + * shared stage machine lives in alltoallv/alltoallv_push.c; this file only |
| 24 | + * does the alltoall-specific argument parsing. |
| 25 | + * |
| 26 | + * Requirements: |
| 27 | + * - global_memh_dst: peer destination buffer handles pre-exchanged. |
| 28 | + * - No proxies (push writes directly to peer rbuf, not via a proxy rank). |
| 29 | + */ |
| 30 | + |
| 31 | +#include "alltoall.h" |
| 32 | +#include "../alltoallv/alltoallv.h" |
| 33 | + |
| 34 | +ucc_status_t ucc_tl_cuda_alltoall_push_init(ucc_base_coll_args_t *coll_args, |
| 35 | + ucc_base_team_t *tl_team, |
| 36 | + ucc_coll_task_t **task_p) |
| 37 | +{ |
| 38 | + ucc_tl_cuda_team_t *team = ucc_derived_of(tl_team, ucc_tl_cuda_team_t); |
| 39 | + ucc_tl_cuda_task_t *task; |
| 40 | + ucc_coll_args_t *args; |
| 41 | + ucc_status_t status; |
| 42 | + |
| 43 | + if (UCC_IS_INPLACE(coll_args->args)) { |
| 44 | + return UCC_ERR_NOT_SUPPORTED; |
| 45 | + } |
| 46 | + |
| 47 | + status = ucc_tl_cuda_task_init(coll_args, team, &task); |
| 48 | + if (ucc_unlikely(status != UCC_OK)) { |
| 49 | + return status; |
| 50 | + } |
| 51 | + |
| 52 | + args = &TASK_ARGS(task); |
| 53 | + |
| 54 | + /* Uniform counts: chunk = count/nranks, dst offset = rank*chunk — both |
| 55 | + * computable locally, so the SETUP barrier is skipped. */ |
| 56 | + task->alltoallv_push.sbuf = args->src.info.buffer; |
| 57 | + task->alltoallv_push.rbuf = args->dst.info.buffer; |
| 58 | + task->alltoallv_push.sdt = args->src.info.datatype; |
| 59 | + task->alltoallv_push.rdt = args->dst.info.datatype; |
| 60 | + task->alltoallv_push.scnts = NULL; |
| 61 | + task->alltoallv_push.rcnts = NULL; |
| 62 | + task->alltoallv_push.sdispl = NULL; |
| 63 | + task->alltoallv_push.rdispl = NULL; |
| 64 | + task->alltoallv_push.needs_setup = 0; |
| 65 | + task->alltoallv_push.global_memh_dst = args->dst_memh.global_memh; |
| 66 | + |
| 67 | + status = ucc_tl_cuda_alltoallv_push_setup(task); |
| 68 | + if (ucc_unlikely(status != UCC_OK)) { |
| 69 | + goto err; |
| 70 | + } |
| 71 | + |
| 72 | + *task_p = &task->super; |
| 73 | + return UCC_OK; |
| 74 | +err: |
| 75 | + ucc_tl_cuda_task_put(task); |
| 76 | + return status; |
| 77 | +} |
0 commit comments