【问题标题】:Segmentation fault 11: C with MPI分段错误 11:带有 MPI 的 C
【发布时间】:2015-10-03 22:43:31
【问题描述】:

使用 MPI 实现生命游戏的并行版本,出现分段错误(信号 11)。 MPI 的新手,无法真正让 valgrind 告诉我错误究竟存在于何处。简化我的代码,发现加粗的sn-p有问题。

编辑:标记出问题所在的代码块

#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 坐标。

【问题讨论】:

  • 你做了什么来简化应用程序以便了解分段错误发生的位置?首先简化,然后添加更多代码来检测确切的故障位置。
  • 我在没有进行域分解的情况下实现了一个并行版本。所以基本上这是没有 MPI_send / MPI_receive 的部分,也是我垂直划分域的部分。
  • 不过,添加更多关于您尝试过的内容的详细信息,并尝试在您的问题中更加具体。这不是一个您可以转储代码并让人们解决您的问题的平台。
  • 更新您的问题以指明问题出现的位置。请找出它出现的代码块或行。也许有人会帮忙。
  • argv[1] 应该是什么意思?我没有看到它在任何地方被引用。提示:C 使用零偏移。 (for(i=1; i&lt;=r; i++){ 也假设一个偏移量,恕我直言。

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


【解决方案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
  }
}

由于正在访问的索引,注释指示的所有行都会导致段错误。您的循环分别给出ij1r1c 包括在内。但是,仅访问old 数组中的元素0r-1new 数组中的元素0r-1 才有效。为了解决这个问题,我们可以将外循环更改为

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

值得注意的是,这不会将每个值都设置为 new。您可能打算将old 声明为大小row,即r+1。在这种情况下,您可以在此循环中将继续条件设为 i &lt; r。不过,我不确定情况是否如此。这会处理访问old 中的元素所产生的错误。

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

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

还有两个问题我想指出:

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

  2. 当您计算 seg = c/nprocs 时,此除法的余数将被忽略。有 250 列,但 seg 变为 250/4 = 62(通过整数除法),并且 62*4 = 248,小于列的全部数量。您需要确保将非干净的列划分为进程。

【讨论】:

  • 解决了这个问题。非常感谢克拉丽莎。问题确实出在 for 循环中。从 i 2) 进程。所以希望我能对这个答案投赞成票,但stackoverflow不会让我这样做(
  • 还发现 C 不会自动为多维数组分配连续的内存位置。所以我这样做了,` int data = (int *)malloc(rowscolssizeof(int)); int **array= (int **)malloc(rowssizeof(int*)); for (i=0; i
  • 没问题@Aditya。如果您有 gcc 4.8 或更高版本,我建议使用 Address Sanitizer 来调试此类问题,因为它可以准确地告诉您内存问题在哪里。介绍见clang.llvm.org/docs/AddressSanitizer.html;您不需要使用 clang,尽管此文档适用于 clang。
猜你喜欢
  • 2015-01-26
  • 2017-06-25
  • 2016-05-08
  • 2017-07-30
  • 1970-01-01
  • 2016-10-09
  • 2018-03-27
  • 2016-06-08
  • 2014-04-12
相关资源
最近更新 更多