@@ -15,6 +15,23 @@ __device__ __forceinline__ std::pair<int, int> get_buffer_offset(
1515 return dst_rank_idx == next_rank_idx ? std::make_pair (0 , 1 ) : std::make_pair (1 , 0 );
1616}
1717
18+ template <int kNumQPs >
19+ __device__ __forceinline__ std::pair<int64_t , int > get_pp_send_chunk (
20+ const int & qp_idx, const int64_t & num_bytes) {
21+ EP_STATIC_ASSERT (kNumQPs > 0 , " Invalid number of QPs" );
22+
23+ const auto num_tma_blocks = num_bytes / ptx::kNumTMAAlignBytes ;
24+ const auto num_tma_blocks_per_qp = math::ceil_div<int64_t >(num_tma_blocks, static_cast <int64_t >(kNumQPs ));
25+ const auto start_block_idx = static_cast <int64_t >(qp_idx) * num_tma_blocks_per_qp;
26+ if (start_block_idx >= num_tma_blocks)
27+ return {0 , 0 };
28+
29+ const auto end_block_idx = std::min (start_block_idx + num_tma_blocks_per_qp, num_tma_blocks);
30+ const auto offset = start_block_idx * ptx::kNumTMAAlignBytes ;
31+ const auto chunk_bytes = static_cast <int >((end_block_idx - start_block_idx) * ptx::kNumTMAAlignBytes );
32+ return {offset, chunk_bytes};
33+ }
34+
1835template <int64_t kNumTimeoutCycles , typename timeout_print_t >
1936__device__ __forceinline__ void check_signal (
2037 const handle::NCCLGin& gin,
@@ -113,6 +130,7 @@ __device__ __forceinline__ void tma_copy(
113130template <int kNumSMs ,
114131 int kNumRanks ,
115132 int kNumSmemBytes ,
133+ int kNumQPs ,
116134 int64_t kNumTimeoutCycles >
117135__global__ void __launch_bounds__ (32 , 1 )
118136pp_send_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
@@ -126,7 +144,7 @@ pp_send_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
126144 const auto [local_idx_in_dst, dst_idx_in_local] = get_buffer_offset<kNumRanks >(rank_idx, dst_rank_idx);
127145
128146 // Gin handle
129- const auto gin = handle::NCCLGin (nccl_dev_comm, nccl_window, 0 , NCCL_GIN_RESOURCE_SHARING_CTA );
147+ const auto signal_gin = handle::NCCLGin (nccl_dev_comm, nccl_window, 0 , NCCL_GIN_RESOURCE_SHARING_CTA );
130148
131149 // Buffer offsets
132150 const auto send_count_ptr = workspace_layout.get_pp_send_count_ptr (dst_idx_in_local);
@@ -140,7 +158,7 @@ pp_send_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
140158 // Wait buffer slot release and do TMA
141159 if (ptx::elect_one_sync ()) {
142160 check_signal<kNumTimeoutCycles >(
143- gin ,
161+ signal_gin ,
144162 static_cast <ncclGinSignal_t>(kNumRanks + dst_idx_in_local + 2 ),
145163 send_count - num_max_inflight_tensors + 1 ,
146164 // TODO: print more info, and control the SM who prints it
@@ -150,22 +168,36 @@ pp_send_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
150168 }
151169 cooperative_groups::this_grid ().sync ();
152170
153- // Issue RDMA put
171+ // Issue RDMA puts across all requested QPs. Each QP signals completion independently,
172+ // because there is no cross-QP ordering guarantee.
173+ if (ptx::elect_one_sync ()) {
174+ for (int qp_idx = sm_idx; qp_idx < kNumQPs ; qp_idx += kNumSMs ) {
175+ const auto [offset, chunk_bytes] = get_pp_send_chunk<kNumQPs >(qp_idx, num_x_bytes);
176+ const auto data_gin = handle::NCCLGin (nccl_dev_comm, nccl_window, qp_idx, NCCL_GIN_RESOURCE_SHARING_CTA );
177+ const auto recv_signal = static_cast <ncclGinSignal_t>(kNumRanks + local_idx_in_dst);
178+
179+ if (chunk_bytes == 0 ) {
180+ data_gin.signal <ncclTeamTagWorld>(dst_rank_idx, ncclGin_SignalInc{recv_signal});
181+ } else {
182+ data_gin.put <ncclTeamTagWorld>(
183+ math::advance_ptr (recv_buffer_ptr, offset),
184+ math::advance_ptr (send_buffer_ptr, offset),
185+ chunk_bytes, dst_rank_idx,
186+ 0 ,
187+ ncclGin_SignalInc{recv_signal});
188+ }
189+ }
190+ }
191+
154192 if (sm_idx == 0 and ptx::elect_one_sync ()) {
155- gin.put <ncclTeamTagWorld>(
156- recv_buffer_ptr,
157- send_buffer_ptr,
158- num_x_bytes, dst_rank_idx,
159- 0 ,
160- // TODO: is this signal highly optimized?
161- ncclGin_SignalInc (static_cast <ncclGinSignal_t>(local_idx_in_dst + kNumRanks )));
162193 *send_count_ptr += 1 ;
163194 }
164195}
165196
166197template <int kNumSMs ,
167198 int kNumRanks ,
168199 int kNumSmemBytes ,
200+ int kNumQPs ,
169201 int64_t kNumTimeoutCycles >
170202__global__ void __launch_bounds__ (32 , 1 )
171203pp_recv_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
@@ -178,8 +210,9 @@ pp_recv_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
178210 const auto workspace_layout = layout::WorkspaceLayout (workspace, 1 , kNumRanks , 0 );
179211 const auto [src_idx_in_local, local_idx_in_src] = get_buffer_offset<kNumRanks >(src_rank_idx, rank_idx);
180212
181- // Gin handle
182- const auto gin = handle::NCCLGin (nccl_dev_comm, nccl_window, 0 , NCCL_GIN_RESOURCE_SHARING_CTA );
213+ // QP 0 is used for release notification. Payload completion signals are
214+ // polled on the same QP/context that issued each RDMA put.
215+ const auto signal_gin = handle::NCCLGin (nccl_dev_comm, nccl_window, 0 , NCCL_GIN_RESOURCE_SHARING_CTA );
183216
184217 // Buffer offsets
185218 const auto recv_count_ptr = workspace_layout.get_pp_recv_count_ptr (src_idx_in_local);
@@ -190,21 +223,29 @@ pp_recv_impl(const ncclDevComm_t nccl_dev_comm, const ncclWindow_t nccl_window,
190223
191224 // Copy from the buffer into a new tensor
192225 if (ptx::elect_one_sync ()) {
193- check_signal<kNumTimeoutCycles >(
194- gin,
195- static_cast <ncclGinSignal_t>(src_idx_in_local + kNumRanks ),
196- recv_count + 1 ,
197- // TODO: print more info, and control the SM who prints it
198- []() { printf (" DeepEP PP recv timeout, recv buffer is empty\n " ); }
199- );
226+ for (int qp_idx = sm_idx; qp_idx < kNumQPs ; qp_idx += kNumSMs ) {
227+ const auto data_gin = handle::NCCLGin (nccl_dev_comm, nccl_window, qp_idx, NCCL_GIN_RESOURCE_SHARING_CTA );
228+ check_signal<kNumTimeoutCycles >(
229+ data_gin,
230+ static_cast <ncclGinSignal_t>(src_idx_in_local + kNumRanks ),
231+ recv_count + 1 ,
232+ // TODO: print more info, and control the SM who prints it
233+ []() { printf (" DeepEP PP recv timeout, recv buffer is empty\n " ); }
234+ );
235+ }
236+ }
237+ cooperative_groups::this_grid ().sync ();
238+
239+ if (ptx::elect_one_sync ()) {
200240 tma_copy<kNumSMs , kNumSmemBytes >(recv_buffer_ptr, x, num_x_bytes, sm_idx);
201241 }
202242 cooperative_groups::this_grid ().sync ();
203243
204244 // TODO: add a comment
205245 if (sm_idx == 0 and ptx::elect_one_sync ()) {
206- gin.signal <ncclTeamTagWorld>(
207- src_rank_idx, ncclGin_SignalInc (static_cast <ncclGinSignal_t>(kNumRanks + local_idx_in_src + 2 ))
246+ signal_gin.signal <ncclTeamTagWorld>(
247+ src_rank_idx,
248+ ncclGin_SignalInc (static_cast <ncclGinSignal_t>(kNumRanks + 2 + local_idx_in_src))
208249 );
209250 *recv_count_ptr += 1 ;
210251 }
0 commit comments