使用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坐标,表示活细胞。
答案 0 :(得分:1)
正如wildplasser在this comment中暗示的那样,崩溃源于你在这里的循环(已经过去了,你指出问题源于此):
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
。但是,仅在0
数组中访问元素r-1
到old
以及0
数组中的元素r-1
到new
才有效。要解决这个问题,我们可以将外部循环更改为
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
之间访问{1}}和old[i]
(类似new[i]
和0
),程序不会崩溃。这是通过将r-1
(或i < ghost
保留在主循环中)并为每个循环保留< ghost - 1
(或类似地,j < r
)来完成的。但是,这几乎肯定不会给你想要的行为。看起来您的当前版本的循环用于串行程序,并忽略您尝试通过将列拆分为< r -1
大小的片段而引入的并行性。循环需要完全重新设计,以便它们的访问使用程序中的并行性,并且进程进行通信,以便跨不同处理器的相邻单元具有适当的通信。这需要对计划进行重大改革。
我还想指出另外两个问题:
如果您在2个以上的进程上运行程序,程序会挂起,因为发送和接收是硬编码的,只能在进程0和进程1之间进行通信。您需要确保进程2及以上要么不要&# 39;尝试发送/接收,或者在发送/接收时实际通信。
当您计算seg
时,此分区的其余部分将被忽略。有250列,但seg变为250/4 = 62(按整数除法),62 * 4 = 248,小于完整列数。您需要确保将非清洁的列划分为流程。