使用非常大的阵列时MPI分段错误错误

时间:2011-11-18 12:59:10

标签: arrays sum mpi

我正在尝试用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;

}

2 个答案:

答案 0 :(得分:1)

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

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

::std::vector<int>的地址传递给期望该参数中有void*的函数。允许将任何指针类型(如::std::vector<int>*)转换为void*作为隐式转换,因此此时不存在编译错误。但是,MPI_Scatterv期望它的第一个参数是发送缓冲区的地址,MPI期望它是一个正常的数组。

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

::std::vector<int>的更改导致实际数据被放置在堆上,而这个数据的最大大小要大得多(在64位系统上,您可以期望更早地耗尽物理内存)。实际上,您已经在几行之前实现了针对特定问题的解决方案:

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

在这里,您访问一个元素,然后获取其地址(请注意[] binds tighter而不是&)。还行吧。只要您不修改基础vector。如果您只是将该解决方案应用于上一个调用,则可以非常轻松地解决此问题:

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

在任何情况下,除了两个vector对象之外,您的代码看起来像是为旧C标准而不是C ++编写的 - 例如,您可能会考虑查看{{3}这样的内容}而不是malloc.h,您可以将变量声明与其定义一致(甚至在new operator family内!),使用ostream for loop headers而不是{来缓解您的生活{1}} ...

答案 1 :(得分:0)

程序在我看来是一个C,因为你没有使用任何C ++工具,或任何标题(本来是 cstdio ,没有 .h )。

无论如何,你可以用标准配置替换数组分配A [非常大的数字]吗?如果你想要C, malloc ,否则。然后发布结果。

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

让我知道。