分段故障11:C与MPI

时间:2015-10-03 22:43:32

标签: c parallel-processing segmentation-fault mpi openmpi

使用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坐标,表示活细胞。

1 个答案:

答案 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
  }
}

由于要访问索引,注释所指示的所有行都会导致段错误。您的循环分别通过ij1提供r1c。但是,仅在0数组中访问元素r-1old以及0数组中的元素r-1new才有效。要解决这个问题,我们可以将外部循环更改为

for(i = 1; i < r-1; i++){
    //...code here...
}

值得注意的是,这不会将每个值设置为新值。您可能需要声明old的大小为row,即r+1。在这种情况下,您可以在此循环中将延续条件设为i < r。不过,我不确定是不是这样。这可以处理因访问old中的元素而产生的错误。

但是,使用oldold1d仍然存在问题。 old中包含r(250)个元素,但在这种情况下,唯一的ghost(63)条目是初始化的。 old1d永远不会初始化任何非实时值,并且会使用MPI_SendMPI_Recv发送未初始化的值。您需要选择old数组的大小,并确保初始化其中的所有值,并选择old1d数组的大小并确保其所有值都已初始化。 newnew1d也是如此。

通过更改循环,以便始终在索引oldnew之间访问0ghost-1,并更改所有循环,以便{的每个元素始终在oldnew之间访问{1}}和old[i](类似new[i]0),程序不会崩溃。这是通过将r-1(或i < ghost保留在主循环中)并为每个循环保留< ghost - 1(或类似地,j < r)来完成的。但是,这几乎肯定不会给你想要的行为。看起来您的当前版本的循环用于串行程序,并忽略您尝试通过将列拆分为< r -1大小的片段而引入的并行性。循环需要完全重新设计,以便它们的访问使用程序中的并行性,并且进程进行通信,以便跨不同处理器的相邻单元具有适当的通信。这需要对计划进行重大改革。

我还想指出另外两个问题:

  1. 如果您在2个以上的进程上运行程序,程序会挂起,因为发送和接收是硬编码的,只能在进程0和进程1之间进行通信。您需要确保进程2及以上要么不要&# 39;尝试发送/接收,或者在发送/接收时实际通信。

  2. 当您计算seg时,此分区的其余部分将被忽略。有250列,但seg变为250/4 = 62(按整数除法),62 * 4 = 248,小于完整列数。您需要确保将非清洁的列划分为流程。