发送具有std :: vector成员的struct时出现分段错误

时间:2016-10-16 10:28:50

标签: c++ linux mpi c++14 openmpi

为什么我使用BranchID Product EXB-Teller 3 HOF-Dealer 1 HOF-Remit 1 SUM Stock AVG Stock 1 Bahraini Dinar 52759.252 25085.631 102.500 77947.383 25982.461000 1 Egiptian Pounds 100.000 100.000 NULL 200.000 100.000000 命令获取以下代码的以下错误?我在调整mpirun -np 2 ./out大小后调用了make_layout(),所以通常我不应该得到这个错误。如果我不调整大小,它可以工作。是什么原因?

main.cpp中:

std::vector

错误讯息:

#include <iostream>
#include <vector>
#include "mpi.h"

MPI_Datatype MPI_CHILD;

struct Child
{
    std::vector<int> age;

    void make_layout();
};

void Child::make_layout()
{
    int nblock = 1;
    int age_size = age.size();
    int block_count[nblock] = {age_size};
    MPI_Datatype block_type[nblock] = {MPI_INT};
    MPI_Aint offset[nblock] = {0};
    MPI_Type_struct(nblock, block_count, offset, block_type, &MPI_CHILD);
    MPI_Type_commit(&MPI_CHILD);
}

int main()
{
    int rank, size;

    MPI_Init(NULL, NULL);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);    

    Child kid;
    kid.age.resize(5);
    kid.make_layout();
    int datasize;
    MPI_Type_size(MPI_CHILD, &datasize);
    std::cout << datasize << std::endl; // output: 20 (5x4 seems OK).

    if (rank == 0)
    {
        MPI_Send(&kid, 1, MPI_CHILD, 1, 0, MPI_COMM_WORLD);
    }

    if (rank == 1)
    {
        MPI_Recv(&kid, 1, MPI_CHILD, 0, 0, MPI_COMM_WORLD, NULL);
    }

    MPI_Finalize();

    return 0;
}

3 个答案:

答案 0 :(得分:1)

这里的问题是你告诉MPI从scala> if (true) 1 res2: AnyVal = 1 scala> if (true) 1 res3: AnyVal = 1 scala> if (true) 1 else 2 res4: Int = 1 发送一个整数块,但这不是你的数据所在。 &kid指向&kid对象,该对象具有指向堆上某处的整数块的内部指针。

std::vector替换为&kid,它应该有效。当你不调整大小时它“工作”的原因是向量将是0大小,因此MPI将尝试发送空消息并且不会发生实际的内存访问。

答案 1 :(得分:1)

以下是一些使用具有绝对地址的MPI数据类型的std::vector个成员的示例:

struct Child
{
    int foo;
    std::vector<float> bar;
    std::vector<int> baz;

    Child() : dtype(MPI_DATATYPE_NULL) {}
    ~Child() { if (dtype != MPI_DATATYPE_NULL) MPI_Type_free(dtype); }

    const MPI_Datatype mpi_dtype();
    void invalidate_dtype();

private:
    MPI_Datatype dtype;
    void make_dtype();
};

const MPI_Datatype Child::mpi_dtype()
{
    if (dtype == MPI_DATATYPE_NULL)
        make_dtype();
    return dtype;
}

void Child::invalidate_dtype()
{
    if (dtype != MPI_DATATYPE_NULL)
        MPI_Datatype_free(&dtype);
}

void Child::make_dtype()
{
    const int nblock = 3;
    int block_count[nblock] = {1, bar.size(), baz.size()};
    MPI_Datatype block_type[nblock] = {MPI_INT, MPI_FLOAT, MPI_INT};
    MPI_Aint offset[nblock];
    MPI_Get_address(&foo, &offset[0]);
    MPI_Get_address(&bar[0], &offset[1]);
    MPI_Get_address(&baz[0], &offset[2]);

    MPI_Type_struct(nblock, block_count, offset, block_type, &dtype);
    MPI_Type_commit(&dtype);
}

该类的示例使用:

Child kid;
kid.foo = 5;
kid.bar.resize(5);
kid.baz.resize(10);

if (rank == 0)
{
    MPI_Send(MPI_BOTTOM, 1, kid.mpi_dtype(), 1, 0, MPI_COMM_WORLD);
}

if (rank == 1)
{
    MPI_Recv(MPI_BOTTOM, 1, kid.mpi_dtype(), 0, 0, MPI_COMM_WORLD, NULL);
}

请注意使用MPI_BOTTOM作为缓冲区地址。 MPI_BOTTOM指定地址空间的底部,在具有平坦地址空间的体系结构上为0。由于传递给MPI_Type_create_struct的偏移量是结构成员的绝对地址,当这些地址被添加到0时,结果再次是每个结构成员的绝对地址。 Child::mpi_dtype()返回特定于该实例的延迟构造的MPI数据类型。

由于resize()重新分配内存,这可能导致数据移动到内存中的其他位置,因此invalidate_dtype()方法应该用于强制在{{1}之后重新创建MPI数据类型或任何其他可能触发内存重新分配的操作:

resize()

请原谅上面任何草率的C ++代码。

答案 2 :(得分:0)

小心,你遇到了几个问题。

第一个 std::vector将对象存储在堆中,因此数据并不真正存储在您的结构中。

第二您甚至无法在动态库之间发送STL容器,对于应用实例也是如此。因为它们可能使用不同版本的STL进行编译,并且在不同的体系结构上工作方式不同。

以下是关于这部分问题的好答案:https://stackoverflow.com/a/22797419/440168