p2pComm
-
initialized: if init or not
-
rc_handles: qp info, including qpn, lid
-
rc_handles_len: Size of rc_handles, double size of local handles.
-
mem_handle: buff register info, including lkey, rkey
struct nvshmemt_ib_common_mem_handle { struct ibv_mr *mr; void *buf; int fd; uint32_t lkey; uint32_t rkey; bool local_only; }; -
nvshmemi_ibgda_device_state: some config of ibgda device
-
p2pcomm_device_state_d: using for transport(important)
-
trans: including some api for call function
-
pe: server 0; client 1
-
num_dci_eps: number of dci eps
P2p完整流程
connect
- 创建p2pComm结构体,init时create qp
- 本地的qp handles信息(use for connect endpoints, created by create_qp)
- buff地址
- 注册buff后的mem_handle信息,包括lkey rkey等
- load对端的qp handles并调用connect_endpoints建联
- load对端的buf地址与mem_handle信息保存(注册的内存信息)
connect后,需要sleep来保证两侧均完成建联后才能收发,如果有mpirun的话可以尝试barrier
send
-
ibgda_put_nbi_warp(…)
-
amo_non_fetch_add(…)
__global__ void p2p_client_put_kernel(void *remote_ptr, void *local_buf, size_t size, int dst_pe, p2pcomm_ibgda_device_state_t *state, int iteration) { const int lane_id = threadIdx.x % 32; const int warp_id = threadIdx.x / 32; const int num_warps = blockDim.x / 32; size_t chunk_size = (size + num_warps - 1) / num_warps; size_t offset = warp_id * chunk_size; size_t data_size; if (offset < size) { data_size = min(chunk_size, size - offset); // 执行 put ibgda_p2p::nvshmemi_ibgda_put_nbi_warp( (uint64_t)remote_ptr + offset, (uint64_t)local_buf + offset, data_size, dst_pe, warp_id, lane_id, 0, state); } else { data_size = 0; // ❗越界的 warp 不处理任何数据 } __threadfence_system(); if (lane_id == 0) { ibgda_p2p::nvshmemi_ibgda_amo_nonfetch_add((int *)remote_ptr + size / sizeof(int) + warp_id + 1, 1, dst_pe, warp_id, state); } __threadfence_system(); if (warp_id == 0 && lane_id == 0) { int local_flag = 0; float *f_recv_buf = (float *)local_buf; // 正确的类型解释 while (f_recv_buf[size / 4] != 1 + iteration) { ibgda_p2p::nvshmemi_ibgda_get_nbi_warp( (uint64_t)remote_ptr + size, (uint64_t)local_buf + size, 4, dst_pe, warp_id, lane_id, 0, state); __threadfence_system(); auto qp = ibgda_p2p::ibgda_get_rc(dst_pe, 0, state); ibgda_p2p::ibgda_quiet(qp, state); } } __threadfence_system(); }
recv
-
p2p_server_poll_kernel(…) just wait for flag_ptr to be writen
__global__ void p2p_server_poll_kernel(void *recv_buf, size_t size, int iteration) { const int lane_id = threadIdx.x % 32; const auto warp_id = threadIdx.x / 32; if (lane_id == 0) { int *flag_ptr = (int *)recv_buf + warp_id + size / sizeof(int) + 1; // printf("before get value flag %d value %d\\n ",*flag_ptr , (iteration+1)*1073741824); while (ibgda_p2p::ld_acquire_global(flag_ptr) != iteration + 1) { } ((float *)recv_buf)[size / 4] = iteration + 1; } __threadfence_system(); }
Example
client
p2p_client_put_kernel<<<1, threads>>>(remote_ptr, local_buf, buf_size, 0, state, i);第一个参数是block数量,意为发送给几个不同的server
第二个参数为每个block的threads数量,其中threads/32得到warp数量,每个warp负责一部分buffer的发送,每个warp负责一部分chunk的发送,每个warp的第一个thread在前面的warp都做完操作以后,执行一次amo
server
p2p_server_poll_kernel<<<1, threads>>>(local_buf, buf_size, i);每个warp中的第一天thread等待flag成功被改写