MPI多节点运行报错MPI_ERR_TRUNCATE:C语言并行代码求助
我编写了一段用于多项式数组计算的C语言MPI代码,单节点运行16及以下任务时正常,但使用2节点共32个任务(每节点16个)运行时,出现如下错误:
[06:272259] *** An error occurred in MPI_Recv [06:272259] *** reported
by process [2965045249,0] [06:272259] *** on communicator
MPI_COMM_WORLD [06:272259] *** MPI_ERR_TRUNCATE: message truncated
[06:272259] *** MPI_ERRORS_ARE_FATAL (processes in this communicator
will now abort, [06:272259] *** and potentially your MPI job)
[07][[45243,1],31][btl_tcp.c:559:mca_btl_tcp_recv_blocking] recv(20)
failed: Connection reset by peer (104)
代码如下:
int main(int argc, char **argv) { int id; int n; int i, size, arraySize; double *vet, valor, *vresp, resposta, tempo, a[GRAU + 1]; int hostsize; char hostname[MPI_MAX_PROCESSOR_NAME]; MPI_Status status; MPI_Init(&argc, &argv); MPI_Get_processor_name(hostname, &hostsize); MPI_Comm_rank(MPI_COMM_WORLD, &id); MPI_Comm_size(MPI_COMM_WORLD, &n); if (id == 0) // Master { MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&a, GRAU, MPI_DOUBLE, 0, MPI_COMM_WORLD); for (size = TAM_INI; size <= TAM_MAX; size += TAM_INC) { tempo = -MPI_Wtime(); for (int dest = 1; dest < n; ++dest) { int ini = 0; int fim = dest * size / (n - 1); int tam = fim - ini; MPI_Send(&ini, 1, MPI_INT, dest, 0, MPI_COMM_WORLD); MPI_Send(&tam, 1, MPI_INT, dest, 0, MPI_COMM_WORLD); MPI_Send(&x[ini], tam, MPI_DOUBLE, dest, 0, MPI_COMM_WORLD); ini = fim; fflush(stdout); } int total = 0; for (int dest = 1; dest < n; ++dest) { int ini_escravo; int tam_escravo; MPI_Recv(&ini_escravo, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); MPI_Recv(&tam_escravo, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); MPI_Recv(&y[ini_escravo], tam_escravo, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); } tempo += MPI_Wtime(); } } else { // Slave MPI_Barrier(MPI_COMM_WORLD); MPI_Bcast(&a, GRAU, MPI_DOUBLE, 0, MPI_COMM_WORLD); for (arraySize = TAM_INI; arraySize <= TAM_MAX; arraySize += TAM_INC) { int ini, tam; MPI_Recv(&ini, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); MPI_Recv(&tam, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); MPI_Recv(&x[0], tam, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &status); for (i = 0; i < tam; ++i) y[i] = polinomio(a, GRAU, x[i]); MPI_Send(&ini, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Send(&tam, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Send(&y[0], tam, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); fflush(stdout); } } MPI_Finalize(); return 0; }
1. 数据划分逻辑错误
主节点发送数据的循环中,ini被定义为循环内的局部变量,每次循环都会重置为0,导致所有从节点收到的都是从索引0开始的重叠数据切片,且后续进程计算的tam可能远超出合理范围,引发内存越界和数据截断。
修复代码:将ini移到循环外,保证切片范围连续不重叠:
// 主节点发送循环修改为: int ini = 0; for (int dest = 1; dest < n; ++dest) { int fim = dest * size / (n - 1); int tam = fim - ini; MPI_Send(&ini, 1, MPI_INT, dest, 0, MPI_COMM_WORLD); MPI_Send(&tam, 1, MPI_INT, dest, 0, MPI_COMM_WORLD); MPI_Send(&x[ini], tam, MPI_DOUBLE, dest, 0, MPI_COMM_WORLD); ini = fim; fflush(stdout); }
2. MPI_Bcast元素数量不匹配
代码中a数组定义为double a[GRAU + 1](包含GRAU+1个系数),但MPI_Bcast只发送了GRAU个元素,导致最后一个多项式系数未传递,跨节点时内存布局差异会放大这个问题,引发不可预期的内存错误。
修复代码:调整Bcast的元素计数为GRAU + 1:
MPI_Bcast(&a, GRAU + 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
3. 从节点缓冲区未初始化
从节点直接使用x[0]和y[0]但未分配内存,单节点时可能因栈空间侥幸正常,跨节点时会出现内存访问越界,导致接收数据截断或进程崩溃。
修复代码:从节点根据接收的tam动态分配缓冲区:
// 从节点循环内修改为: int ini, tam; MPI_Recv(&ini, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); MPI_Recv(&tam, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); // 动态分配内存 double *x = malloc(tam * sizeof(double)); double *y = malloc(tam * sizeof(double)); MPI_Recv(x, tam, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &status); for (i = 0; i < tam; ++i) y[i] = polinomio(a, GRAU, x[i]); MPI_Send(&ini, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Send(&tam, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); MPI_Send(y, tam, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); fflush(stdout); // 释放内存 free(x); free(y);
同时需确保主节点的x、y数组已分配足够内存(至少size个元素)。
4. 接收消息的来源不匹配
主节点使用MPI_ANY_SOURCE接收时,三次MPI_Recv可能来自不同进程,导致ini_escravo、tam_escravo和结果数据不对应,引发数据错乱和截断错误。
修复代码:接收第一个消息后,固定后续消息的来源为同一进程:
// 主节点接收循环修改为: for (int dest = 1; dest < n; ++dest) { int ini_escravo; int tam_escravo; MPI_Recv(&ini_escravo, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); int source = status.MPI_SOURCE; // 获取当前消息的发送进程 MPI_Recv(&tam_escravo, 1, MPI_INT, source, 0, MPI_COMM_WORLD, &status); MPI_Recv(&y[ini_escravo], tam_escravo, MPI_DOUBLE, source, 0, MPI_COMM_WORLD, &status); }
内容的提问来源于stack exchange,提问作者GabrieRabelo

