【问题标题】:MPI Segmentation fault error when use very large array使用非常大的数组时出现 MPI 分段错误错误
【发布时间】:2012-01-01 05:45:29
【问题描述】:

我正在尝试用 C++ 编写一个 MPI 程序,以对一个非常大的数组的值求和。 下面的代码适用于高达 100 万个数组维度,但是当我尝试使用 1000 万个或更多元素执行时,我收到一个 sigmentation 错误。有人可以帮助我吗?谢谢

#include <stdio.h>
#include "mpi.h"

int main(int argc, char *argv[]) {
    double t0, t1, time; //variabili per il calcolo del tempo
int nprocs, myrank;
    int root=0;
long temp, sumtot, i, resto, svStartPos, dim, intNum;

//Dimensione del vettore contenente i valori da sommare
const long A_MAX=10000000;

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);


    long vett[A_MAX];
long parsum[B_MAX];

long c=-1;
int displs[nprocs];
int sendcounts[nprocs];

//printf("B_MAX: %ld\n", B_MAX);

//Inviamo (int)(A_MAX/nprocs) elementi tramite una scatter, resto è il 
//numero di elementi restanti che verranno inviati tramite la scatterv
resto= A_MAX % nprocs;
//printf("Resto: %d\n", resto);

//Posizione da cui iniziare lo Scatterv
svStartPos = A_MAX - resto;
//printf("svStartPos: %d\n", svStartPos);

// numero di elementi per processore senza tener conto del resto 
dim= (A_MAX-resto)/nprocs; 
//printf("dim: %d\n", dim);

//Il processore 0 inizializza il vettore totale, del quale vogliamo
//calcolare la somma
if (myrank==0){
    for (i=0; i<A_MAX; i++)
        vett[i]=1;
}

//Ciascun processore inizializza il vettore locale del quale calcoleremo la
//somma parziale dei suoi elementi. tale somma parziale verrà utilizzata
//nell'operazione di reduce
for (i=0; i<B_MAX; i++)
    parsum[i]=-1;

//Ciascun processore inizializza i vettori sendcounts e displs necessari per
//l'operazione di scatterv
for (i=0; i<nprocs; i++){
    if (i<A_MAX-svStartPos){
        //Se il rank del processore è compreso tra 0 e resto ...
        sendcounts[i]=1;            //...verrà inviato 1 elemento di vett...
        displs[i]= svStartPos+i;    //...di posizione svStartPos+i
    }
    else {
        //se il rank del processore è > resto ...
        sendcounts[i]=0;            //...non verrà inviato alcun elemento
        displs[i]= A_MAX;           
    }
}

root = 0;    //Il processore master

sumtot = 0;  //Valore della domma totale degli elementi di vett
temp = 0;    //valore temporaneo delle somme parziali

MPI_Barrier(MPI_COMM_WORLD);

if (A_MAX>=nprocs){
   MPI_Scatter(&vett[dim*myrank], dim, MPI_LONG, &parsum, dim, MPI_LONG, 0, MPI_COMM_WORLD);
   printf("Processore: %d - Scatter\n", myrank);
}

//La scatterv viene effettuata solo dai processori che hanno il rank 
//0<myrank<resto
if (sendcounts[myrank]==1){       
   MPI_Scatterv(&vett,sendcounts,displs,MPI_LONG,&c,1,MPI_LONG,0,MPI_COMM_WORLD); 
   parsum[B_MAX-1]=c; 
   printf("Processore: %d - effettuo la Scatterv\n", myrank);
} 

MPI_Barrier(MPI_COMM_WORLD);

if(myrank==0){
    t0 = MPI_Wtime(); //inizio conteggio tempo
}

for(i=0; i<B_MAX; i++){
    if (parsum[i]!=-1)     
        temp = temp + parsum[i]; //somma degli elementi 
}
printf("Processore: %d - Somma parziale: %ld\n", myrank, temp);

MPI_Barrier(MPI_COMM_WORLD);

//il risultato di somma di ogni processore viene mandato al root che somma 
//i risultati parziali
MPI_Reduce(&temp,&sumtot,1,MPI_LONG,MPI_SUM,root,MPI_COMM_WORLD);

MPI_Barrier(MPI_COMM_WORLD);

if(myrank==0){
    t1 = MPI_Wtime(); //stop al tempo

    //calcolo e stampa del tempo trascorso
    time = 1.e6 * (t1-t0);
    printf("NumProcessori: %d  Somma: %ld  Tempo: %f\n", nprocs, sumtot, time);

    //verifica del valore somma. Se è corretto sumtot è pari a 0.
    sumtot = sumtot - A_MAX;
    printf("Verifica: %ld\n", sumtot);
}

MPI_Finalize();

return 0;

}

【问题讨论】:

    标签: arrays sum mpi


    【解决方案1】:

    我发现的第一个真正的错误是这一行:

    MPI_Scatterv(&vett,sendcounts,displs,MPI_LONG,&c,1,MPI_LONG,0,MPI_COMM_WORLD);
    

    它将::std::vector&lt;int&gt; 的地址传递给在该参数中需要void* 的函数。任何指针类型(如::std::vector&lt;int&gt;*)到void* 的转换都允许作为隐式转换,因此此时没有编译错误。但是,MPI_Scatterv 期望它的第一个参数是发送缓冲区的地址,而 MPI 期望它是一个普通数组。

    我猜您最近从注释掉的部分更改了代码,其中vett 是一个数组,并试图通过在您的MPI_Scatterv 调用中添加地址运算符来使您的调用正常工作。原始数组可能在某些时候导致了段错误,因为它是堆栈分配的,并且您用这些怪物用完了堆栈空间(Linux系统上的默认堆栈大小约为 iirc 兆字节,这完全符合该假设 - 用ulimit -s)。

    ::std::vector&lt;int&gt; 的更改导致实际数据被放置在堆上,而堆上的最大大小要大得多(在 64 位系统上,您可能会更早地用完物理内存)。实际上,您在几行之前已经针对您的特定问题实施了解决方案:

    MPI_Scatter(&vett[dim*myrank], dim, MPI_LONG, &parsum, dim, MPI_LONG, 0, MPI_COMM_WORLD);
    

    在这里,您访问一个元素,然后获取它的地址(注意 [] binds tighter&amp; )。还行吧。只要不修改底层vector。如果您只是将该解决方案应用于上一次调用,您可以很容易地解决此问题:

    MPI_Scatterv(&vett[0],sendcounts,displs,MPI_LONG,&c,1,MPI_LONG,0,MPI_COMM_WORLD);
    

    在任何情况下,除了两个 vector 对象之外,您的代码看起来像是为旧的 C 标准而不是 C++ 编写的 - 例如,您可能会考虑查看 new operator family 而不是 @ 987654342@,你可以将你的变量声明与它们的定义保持一致(甚至在for loop headers!),使用ostreamcout而不是printf来简化你的生活......

    【讨论】:

    • 对不起,原来的程序不同。我用我的代码的原始版本更新了这个问题......不幸的是,如果我尝试使用 new 运算符,我会遇到编译错误,因为我在集群上运行这个程序,我不知道它使用哪个版本的编译器.那么..在不支持向量的情况下如何管理这些数组?
    • 如果您可以使用vector 编译代码,您可能可以访问新运算符...。您是否查看了堆栈限制与两个数组的大小... ?
    • 如果我尝试 #include "vector.h" 我收到“没有这样的文件或目录”错误。我不知道堆栈限制,因为我在我大学的集群上运行程序,并且我没有太多的机器细节..
    • 那是因为头文件不是"vector.h"而是&lt;vector&gt;,因为我们讨论的是C++标准库的一部分。你确定你使用的是 C++...?
    【解决方案2】:

    在我看来,这个程序是一个 C 程序,因为您没有使用任何 C++ 工具或任何头文件(如果没有 .h,它将是 cstdio)。

    无论如何,您能否将数组分配 A[非常巨大的数字] 替换为标准分配?如果你想要 C,malloc,否则,new。然后发布结果。

    这似乎是一个堆分配问题 (http://c-faq.com/strangeprob/biglocal.html)。

    告诉我。

    【讨论】:

      猜你喜欢
      • 2014-05-31
      • 2022-10-23
      • 2015-05-01
      • 1970-01-01
      • 2019-05-11
      • 2020-06-08
      • 2020-04-20
      • 2017-06-25
      • 1970-01-01
      相关资源
      最近更新 更多