MPI程序进程数中途异常变更求助:参数≥80时numprocs从4变1
问题分析与解决方案
这个问题的核心原因是栈溢出导致的内存数据损坏——具体来说,你修改后的代码使用了变长数组(VLA),当传入的参数≥80时,数组占用的栈空间超出了系统栈的承载上限,进而覆盖了相邻变量numprocs的内存区域,导致它的值被篡改。
为什么会出现这个问题?
我们来拆解修改前后的关键差异:
- 原代码(正常运行):
MAXSIZE是宏定义(#define MAXSIZE 99),数组data[MAXSIZE]是编译期确定大小的固定数组,分配在栈上。99个int(按4字节计算仅396字节)完全在栈的承载范围内,不会触发溢出。 - 修改后的代码(出问题):你把
MAXSIZE改成了局部变量,并通过命令行参数动态设置大小。此时data[MAXSIZE]变成了变长数组(VLA),它的大小在运行时确定,依然分配在栈上。
系统栈的空间通常有限(一般在几MB级别,不同系统配置有差异),当你传入80及以上的参数时,data数组的占用空间加上其他局部变量的开销,超出了栈的可用容量。栈溢出会破坏相邻内存区域的数据,而numprocs作为和data相邻的局部变量,自然会被篡改(你遇到的是变成1,实际也可能是其他随机值)。
如何解决?
最可靠的方案是改用动态内存分配,把数组从栈移到堆上——堆的空间远大于栈,不会有大小限制问题。修改后的代码如下:
#include "mpi.h" #include "string.h" #include "stdio.h" #include "stdlib.h" void main(int argc, char **argv){ int MAXSIZE = 6; int myid, numprocs; int *data, i, x, low, high, myresult, result; // 将data改为指针 char fn[255]; FILE *fp; if (argc > 1) MAXSIZE = atoi(argv[1]); // 动态分配内存,检查分配是否成功 data = (int*)malloc(MAXSIZE * sizeof(int)); if (data == NULL) { fprintf(stderr, "Memory allocation failed!\n"); MPI_Init(&argc, &argv); MPI_Abort(MPI_COMM_WORLD, 1); } MPI_Init (&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank (MPI_COMM_WORLD, &myid); if (myid == 0){ // 打开输入文件并初始化数据 strcpy(fn, getenv("HOME")); strcat (fn, "/MPI/rand_data.txt"); if ((fp = fopen(fn, "r")) == NULL){ printf("Can't open the input file: %s\n\n", fn); free(data); // 释放内存再退出 MPI_Abort(MPI_COMM_WORLD, 1); } for(i = 0; i < MAXSIZE; i++){ fscanf(fp,"%d", &data[i]); } fclose(fp); // 别忘了关闭文件 } // 广播数据 MPI_Bcast (data, MAXSIZE, MPI_INT, 0, MPI_COMM_WORLD); // 计算本地部分的和 x = MAXSIZE/numprocs; low = myid * x; high = low + x; myresult = 0; for(i = low; i < high; i++){ myresult += data[i]; } if (MAXSIZE % numprocs > myid) myresult += data[x*numprocs+myid]; printf("I got %d from %d\n", myresult, myid); // 计算全局和 MPI_Reduce(&myresult, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if (myid == 0) printf("The sum is %d.\n", result); // 释放动态分配的内存 free(data); MPI_Finalize(); }
额外注意细节
- 动态分配内存后一定要检查
malloc的返回值,避免内存分配失败导致崩溃。 - 用完数组后必须调用
free(data)释放内存,避免内存泄漏。 - rank0进程打开文件后要记得
fclose(fp),养成良好的资源回收习惯。
内容的提问来源于stack exchange,提问作者Fariz Awi
相关产品推荐
相关产品推荐

