分段错误 11:带有 MPI 的 C
Segmentation fault 11: C with MPI
正在使用 MPI 实现 Game of Life 的并行版本,出现分段错误(信号 11)。 MPI 的新手,并不能真正让 valgrind 告诉我错误到底在哪里。简化了我的代码,发现粗体代码段存在问题。
编辑:标记了存在问题的代码块
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
if(argc!=5)
printf("Incorrect number of arguments.\n");
else{
// program logic here
int m, n, sum, pid, nprocs;
char outfilename[16];
FILE *outfile;
FILE *infile;
int r=atoi(argv[3]);
int c=atoi(argv[4]);
int gens=atoi(argv[2]);
int **old, **new, *new1d, *old1d;
int i,j;
MPI_Status status;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&pid); //initializing MPI here
//prevented segmentation error by using atoi
//domain decomposition start
// problem arisis here
int seg=c/nprocs; //divide by width
int ghost=seg+1;
int row=r+1;
int tsize=ghost*row; //ghost cells
old1d = malloc(tsize*sizeof(int));
new1d = malloc(tsize*sizeof(int));
old = malloc(r*sizeof(int*));
new = malloc(r*sizeof(int*));
for(i=0; i<ghost; i++){
old[i] = &old1d[i*row];
new[i] = &new1d[i*row];
}
// problem ends
if(pid==0){
MPI_Send(&old[0][seg], c, MPI_INT, 1, 0, MPI_COMM_WORLD);
MPI_Recv(&old[0][ghost],c, MPI_INT, 1, 1, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][1], c, MPI_INT, 1, 2, MPI_COMM_WORLD);
MPI_Recv(&old[0][0], c, MPI_INT, 1, 3, MPI_COMM_WORLD, &status);
}
else{
MPI_Recv(&old[0][0], c, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][1], c, MPI_INT, 0, 1, MPI_COMM_WORLD);
MPI_Recv(&old[0][ghost],c, MPI_INT, 0, 2, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][seg], c, MPI_INT, 0, 3, MPI_COMM_WORLD);
}
infile=fopen(argv[1],"r");
if(infile==NULL){
printf("Could not locate file.\n");
exit(1);
}
while(fscanf(infile,"%d %d",&m, &n)!=EOF){
old[m][n]=1;
}
fclose(infile);
//repeat for number of generations
for(n=0; n<gens; n++){
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
sum = old[i-1][j-1] + old[i-1][j] + old[i-1][j+1]
+ old[i][j-1] + old[i][j+1]
+ old[i+1][j-1] + old[i+1][j] + old[i+1][j+1];
if(sum==2 || sum==3)
new[i][j]=1;
else
new[i][j]=0;
}
}
//copying old state into new state
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
old[i][j] = new[i][j];
}
}
}
//create new output file
sprintf(outfilename,"output_%d",pid);
outfile=fopen(outfilename,"w");
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
if(new[i][j]==1){
fprintf(outfile,"%d\t%d\n",i ,j);
printf("%d %d",i,j);
}
}
}
fclose(outfile);
MPI_Finalize();
}
return 0;
}
编辑:输入文件 life.data.1 具有表示活细胞的 X Y 坐标。
正如 wildplasser 在 中暗示的那样,崩溃源于您在此处的循环(该循环已超出您指出的问题来源):
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
sum = old[i-1][j-1] + old[i-1][j] + old[i-1][j+1]
+ old[i][j-1] + old[i][j+1] //This line causes a segfault
+ old[i+1][j-1] + old[i+1][j] + old[i+1][j+1]; //This line causes a segfault
if(sum==2 || sum==3)
new[i][j]=1; //This line causes a segfault
else
new[i][j]=0; //This line causes a segfault
}
}
注释指示的所有行都由于访问索引而导致段错误。您的循环分别给出 i
和 j
值 1
到 r
和 1
到 c
(含)。但是,仅访问 old
数组中的元素 0
到 r-1
和 new
数组中的元素 0
到 r-1
才有效。要解决此问题,我们可以将外循环更改为
for(i = 1; i < r-1; i++){
//...code here...
}
值得注意的是,这不会设置新的每个值。您可能打算将 old
声明为大小 row
,即 r+1
。在这种情况下,您可以在此循环中将继续条件设置为 i < r
。不过,我不确定情况是否如此。这会处理因访问 old
.
中的元素而产生的错误
但是,old
和old1d
的使用仍然存在问题。 old
中有 r
(250) 个元素,但在这种情况下只有 ghost
(63) 个条目被初始化。 old1d
从未初始化任何非活动值,并且未初始化的值与 MPI_Send
和 MPI_Recv
一起发送。您需要选择 old
数组的大小并确保初始化其中的所有值,还需要选择 old1d
数组的大小并确保其所有值都已初始化。 new
和 new1d
.
也是如此
通过更改循环,使 old
和 new
始终在索引 0
和 ghost-1
之间访问(包括在内),并更改所有循环,以便每个元素old
和 new
(类似于 old[i]
和 new[i]
)总是在 0
和 r-1
之间访问,程序不会崩溃。这是通过在主循环中保留 i < ghost
(或 < ghost - 1
)并在每个循环中保留 j < r
(或类似地,< r -1
)来完成的。但是,这几乎肯定不会提供您想要的行为。看起来您当前版本的循环是为串行程序设计的,并且忽略了您试图通过将列拆分为 seg
大小的块来引入的并行性。循环需要完全重新设计,以便它们的访问使用程序中的并行性,并且进程进行通信,以便跨不同处理器的相邻单元具有适当的通信。这需要对程序进行重大改革。
还有两个问题想指出:
如果您 运行 程序在超过 2 个进程上挂起,因为发送和接收被硬编码为仅在进程 0 和进程 1 之间通信。您需要确保进程 2 及以上进程要么不尝试 send/receive,要么在 send/receive.
时实际进行通信
当您计算 seg = c/nprocs
时,此除法的余数将被忽略。有250列,但是seg变成250/4 = 62(整数除法),62*4 = 248,小于全部列数。您需要确保处理将列不干净地划分为进程的情况。
正在使用 MPI 实现 Game of Life 的并行版本,出现分段错误(信号 11)。 MPI 的新手,并不能真正让 valgrind 告诉我错误到底在哪里。简化了我的代码,发现粗体代码段存在问题。
编辑:标记了存在问题的代码块
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
if(argc!=5)
printf("Incorrect number of arguments.\n");
else{
// program logic here
int m, n, sum, pid, nprocs;
char outfilename[16];
FILE *outfile;
FILE *infile;
int r=atoi(argv[3]);
int c=atoi(argv[4]);
int gens=atoi(argv[2]);
int **old, **new, *new1d, *old1d;
int i,j;
MPI_Status status;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&pid); //initializing MPI here
//prevented segmentation error by using atoi
//domain decomposition start
// problem arisis here
int seg=c/nprocs; //divide by width
int ghost=seg+1;
int row=r+1;
int tsize=ghost*row; //ghost cells
old1d = malloc(tsize*sizeof(int));
new1d = malloc(tsize*sizeof(int));
old = malloc(r*sizeof(int*));
new = malloc(r*sizeof(int*));
for(i=0; i<ghost; i++){
old[i] = &old1d[i*row];
new[i] = &new1d[i*row];
}
// problem ends
if(pid==0){
MPI_Send(&old[0][seg], c, MPI_INT, 1, 0, MPI_COMM_WORLD);
MPI_Recv(&old[0][ghost],c, MPI_INT, 1, 1, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][1], c, MPI_INT, 1, 2, MPI_COMM_WORLD);
MPI_Recv(&old[0][0], c, MPI_INT, 1, 3, MPI_COMM_WORLD, &status);
}
else{
MPI_Recv(&old[0][0], c, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][1], c, MPI_INT, 0, 1, MPI_COMM_WORLD);
MPI_Recv(&old[0][ghost],c, MPI_INT, 0, 2, MPI_COMM_WORLD, &status);
MPI_Send(&old[0][seg], c, MPI_INT, 0, 3, MPI_COMM_WORLD);
}
infile=fopen(argv[1],"r");
if(infile==NULL){
printf("Could not locate file.\n");
exit(1);
}
while(fscanf(infile,"%d %d",&m, &n)!=EOF){
old[m][n]=1;
}
fclose(infile);
//repeat for number of generations
for(n=0; n<gens; n++){
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
sum = old[i-1][j-1] + old[i-1][j] + old[i-1][j+1]
+ old[i][j-1] + old[i][j+1]
+ old[i+1][j-1] + old[i+1][j] + old[i+1][j+1];
if(sum==2 || sum==3)
new[i][j]=1;
else
new[i][j]=0;
}
}
//copying old state into new state
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
old[i][j] = new[i][j];
}
}
}
//create new output file
sprintf(outfilename,"output_%d",pid);
outfile=fopen(outfilename,"w");
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
if(new[i][j]==1){
fprintf(outfile,"%d\t%d\n",i ,j);
printf("%d %d",i,j);
}
}
}
fclose(outfile);
MPI_Finalize();
}
return 0;
}
编辑:输入文件 life.data.1 具有表示活细胞的 X Y 坐标。
正如 wildplasser 在
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
sum = old[i-1][j-1] + old[i-1][j] + old[i-1][j+1]
+ old[i][j-1] + old[i][j+1] //This line causes a segfault
+ old[i+1][j-1] + old[i+1][j] + old[i+1][j+1]; //This line causes a segfault
if(sum==2 || sum==3)
new[i][j]=1; //This line causes a segfault
else
new[i][j]=0; //This line causes a segfault
}
}
注释指示的所有行都由于访问索引而导致段错误。您的循环分别给出 i
和 j
值 1
到 r
和 1
到 c
(含)。但是,仅访问 old
数组中的元素 0
到 r-1
和 new
数组中的元素 0
到 r-1
才有效。要解决此问题,我们可以将外循环更改为
for(i = 1; i < r-1; i++){
//...code here...
}
值得注意的是,这不会设置新的每个值。您可能打算将 old
声明为大小 row
,即 r+1
。在这种情况下,您可以在此循环中将继续条件设置为 i < r
。不过,我不确定情况是否如此。这会处理因访问 old
.
但是,old
和old1d
的使用仍然存在问题。 old
中有 r
(250) 个元素,但在这种情况下只有 ghost
(63) 个条目被初始化。 old1d
从未初始化任何非活动值,并且未初始化的值与 MPI_Send
和 MPI_Recv
一起发送。您需要选择 old
数组的大小并确保初始化其中的所有值,还需要选择 old1d
数组的大小并确保其所有值都已初始化。 new
和 new1d
.
通过更改循环,使 old
和 new
始终在索引 0
和 ghost-1
之间访问(包括在内),并更改所有循环,以便每个元素old
和 new
(类似于 old[i]
和 new[i]
)总是在 0
和 r-1
之间访问,程序不会崩溃。这是通过在主循环中保留 i < ghost
(或 < ghost - 1
)并在每个循环中保留 j < r
(或类似地,< r -1
)来完成的。但是,这几乎肯定不会提供您想要的行为。看起来您当前版本的循环是为串行程序设计的,并且忽略了您试图通过将列拆分为 seg
大小的块来引入的并行性。循环需要完全重新设计,以便它们的访问使用程序中的并行性,并且进程进行通信,以便跨不同处理器的相邻单元具有适当的通信。这需要对程序进行重大改革。
还有两个问题想指出:
如果您 运行 程序在超过 2 个进程上挂起,因为发送和接收被硬编码为仅在进程 0 和进程 1 之间通信。您需要确保进程 2 及以上进程要么不尝试 send/receive,要么在 send/receive.
时实际进行通信
当您计算
seg = c/nprocs
时,此除法的余数将被忽略。有250列,但是seg变成250/4 = 62(整数除法),62*4 = 248,小于全部列数。您需要确保处理将列不干净地划分为进程的情况。