【问题标题】:MPI_Scatter segmentation fault dependent on node numberMPI_Scatter 分段错误取决于节点号
【发布时间】:2017-10-24 02:14:58
【问题描述】:

在为 MPI_Scatter 运行测试代码时出现奇怪的行为。该程序似乎工作正常,但如果节点数大于 4,它会返回分段错误。我使用 mpicxx 编译并使用 mpirun -n N ./a.o. 运行。

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

using std::vector;

int main(void){
    MPI_Init(NULL,NULL);
    int num_PE;
    MPI_Comm_size(MPI_COMM_WORLD, &num_PE);
    int my_PE;
    MPI_Comm_rank(MPI_COMM_WORLD, &my_PE);

    int data_per_PE=2;
    int remainder=0; //conceptually should be less than data_per_PE but shouldn't matter from code perspective
    vector<int> elem_count(num_PE,data_per_PE); //number of elements to scatter
    elem_count[num_PE-1]=data_per_PE+remainder; //let last PE take extra load
    vector<int> start_send(num_PE); //the offset to send from main buffer
    vector<double> small_vec(data_per_PE+remainder); //small place to store values
    vector<double> bigVec; //the big list to distribute to processes
    if (my_PE==0){
        bigVec.reserve(data_per_PE*num_PE+remainder); //make room
        for(int i=0; i<data_per_PE*num_PE+remainder; i++){
            bigVec.push_back(static_cast<double>(i)+1.0); //1,2,3...
            start_send[i]=i*data_per_PE; //the stride
        }
    }
    // MPI_Scatterv(&bigVec[0],&elem_count[0],&start_send[0],MPI_DOUBLE,&small_vec[0],data_per_PE+remainder,MPI_DOUBLE,0,MPI_COMM_WORLD);
    MPI_Scatter(&bigVec[0],data_per_PE,MPI_DOUBLE,&small_vec[0],data_per_PE,MPI_DOUBLE,0,MPI_COMM_WORLD); //scatter
    if (my_PE==0){
        printf("Proc \t elems \n");
    }
    MPI_Barrier(MPI_COMM_WORLD); //let everything catch up before printing
    for (int i=0;i<data_per_PE+remainder;i++){
        printf("%d \t %f \n", my_PE, small_vec[i]); //print the values scattered to each processor
    }

    MPI_Barrier(MPI_COMM_WORLD); //don't think this is necessary but won't hurt
    MPI_Finalize(); //finish

    return 0;
}

【问题讨论】:

    标签: c++ segmentation-fault mpi


    【解决方案1】:

    这个问题与分散无关,而是这一行:

    start_send[i]=i*data_per_PE;
    

    由于i 可以超出num_PE,因此您在start_send 的范围之外写入 - 覆盖了一些可能属于small_vec 的内存。

    这很容易通过创建一个真正最小的示例来找到。

    您的代码中还有另一个问题:&amp;bigVec[0]my_PE!=0 的问题。虽然非根等级忽略了MPI_Scatter 的参数,但该语句涉及取消引用std::vector::operator[] 第一个元素。由于向量是空的,这本身就是未定义的行为。 Here is an explanation 为什么会产生微妙的问题。请改用bigVec.data()

    【讨论】:

    • 不敢相信我错过了,只是假设它与 MPI 相关,因为它随节点而变化。感谢您提供有关 bigVec 取消引用的信息。我不太确定散射函数忽略了多少非根,但很高兴知道 .data() 是安全的。
    【解决方案2】:

    您正在写超过start_send 内部存储的末尾,因此损坏了堆和其中包含的任何其他对象:

    if (my_PE==0){
        bigVec.reserve(data_per_PE*num_PE+remainder); //make room
        for(int i=0; i<data_per_PE*num_PE+remainder; i++){
            bigVec.push_back(static_cast<double>(i)+1.0); //1,2,3...
            start_send[i]=i*data_per_PE; //the stride               <--- HERE
        }
    }
    

    i 一直运行到data_per_PE*num_PE+remainder - 1,但start_send 仅存储num_PE 元素。写到末尾会破坏堆对象的链表,当析构函数试图释放损坏的堆块或访问其他堆对象时,程序可能会出现段错误。

    【讨论】:

    • 感谢您的收获。奇怪的是它没有在较少数量的节点上发生段错误。
    • 没那么奇怪。节点数量越少,覆盖的数据就越少,这可能是堆块的大小最小或者块头对齐,所以损坏是不可见的。
    猜你喜欢
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    相关资源
    最近更新 更多