Cannon矩阵乘法MPI实现初始移位阻塞问题排查
解决Cannon矩阵乘法MPI初始移位停滞问题
以下是针对初始移位环节收发死锁/停滞问题的具体排查和解决步骤:
1. 先验证进程网格与邻居计算的正确性
这是初始移位最常见的错误根源:
- 坐标转换错误:确保进程rank到网格坐标
(i,j)的转换正确(假设是nrow×ncol的方阵网格,且nrow*ncol = 总进程数):int n = sqrt(MPI_COMM_WORLD_SIZE); // 方阵网格大小 int i = rank / n; // 行号 int j = rank % n; // 列号 - 邻居ID的模运算处理:Cannon算法初始移位规则是行i的进程循环左移i步,列j的进程循环上移j步,计算邻居时必须避免负数ID:
必须加// A矩阵行移位:当前进程(i,j)发送A块到左移i步的进程 int dest_a = (j - i + n) % n; // 当前进程接收来自右移i步的进程的A块 int src_a = (j + i) % n; // B矩阵列移位:当前进程(i,j)发送B块到上移j步的进程 int dest_b = (i - j + n) % n; // 当前进程接收来自下移j步的进程的B块 int src_b = (i + j) % n;n再取模,避免j-i或i-j为负数时得到无效的负进程ID。
2. 阻塞收发:用MPI_Sendrecv替代分开的Send/Recv
单独使用MPI_Send和MPI_Recv极易因收发顺序导致死锁(比如两两进程互相等待对方接收),改用原子性的MPI_Sendrecv可自动规避该问题:
// A矩阵初始移位示例 double tmp_A[BLOCK_SIZE][BLOCK_SIZE]; // 临时接收缓冲区 MPI_Sendrecv(A_block, BLOCK_SIZE*BLOCK_SIZE, MPI_DOUBLE, dest_a, 0, tmp_A, BLOCK_SIZE*BLOCK_SIZE, MPI_DOUBLE, src_a, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // 把接收的内容覆盖到原A块 memcpy(A_block, tmp_A, sizeof(tmp_A));
3. 非阻塞收发:缓冲区与请求管理要规范
如果用MPI_Isend/MPI_Irecv,必须注意:
- 缓冲区不能重叠:发送的原块和接收的新块必须用独立缓冲区,不能直接用同一个数组同时做发送和接收缓冲区(否则会覆盖未发送的数据)。
- 确保所有请求被等待:每个收发操作对应一个请求,必须用
MPI_Waitall等待所有请求完成后再操作缓冲区:MPI_Request reqs[2]; double tmp_A[BLOCK_SIZE][BLOCK_SIZE]; // 发起非阻塞收发请求 MPI_Irecv(tmp_A, BLOCK_SIZE*BLOCK_SIZE, MPI_DOUBLE, src_a, 0, MPI_COMM_WORLD, &reqs[0]); MPI_Isend(A_block, BLOCK_SIZE*BLOCK_SIZE, MPI_DOUBLE, dest_a, 0, MPI_COMM_WORLD, &reqs[1]); // 等待所有请求完成 MPI_Waitall(2, reqs, MPI_STATUSES_IGNORE); // 更新A块 memcpy(A_block, tmp_A, sizeof(tmp_A));
4. 调试验证收发配对关系
在初始移位代码前加入调试输出,确认每个进程的发送目标和接收来源是一一对应的:
printf("Rank %d (%d,%d): Send A to %d, Recv A from %d\n", rank, i, j, dest_a, src_a);
运行后检查:如果进程X发送到Y,那么Y的接收来源必须是X,若出现某进程发送的目标没有对应接收方,说明邻居计算逻辑错误。
内容的提问来源于stack exchange,提问作者Vinay Ahir
相关产品推荐
相关产品推荐

