2016-10-16 40 views
1

为什么我用mpirun -np 2 ./out命令得到以下错误代码?我在调整std::vector的大小后调用make_layout(),所以通常我不应该得到这个错误。如果我不调整大小,它会起作用。是什么原因?发送带有std :: vector成员的结构体时出现分段错误

main.cpp中:

#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; 
} 

错误消息:

*** Process received signal *** 
Signal: Segmentation fault (11) 
Signal code: Address not mapped (1) 
Failing at address: 0x14ae7b8 
[ 0] /lib/x86_64-linux-gnu/libpthread.so.0(+0x113d0)[0x7fe1ad91c3d0] 
[ 1] /lib/x86_64-linux-gnu/libc.so.6(cfree+0x22)[0x7fe1ad5c5a92] 
[ 2] ./out[0x400de4] 
[ 3] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fe1ad562830] 
[ 4] ./out[0x400ec9] 
*** End of error message *** 
+0

这可能是我给出的最差的MPI相关建议,但是您可以重载一元'Child :: operator&'返回'age.data()'。 –

+0

'int nblock = 1;'应该是'const int nblock = 1;' –

+0

@ M.M它没有任何区别。 – Shibli

回答

1

下面是几个std::vector成员使用MPI数据类型具有绝对地址的例子:

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()方法应被用来迫使MPI数据类型的娱乐resize()或可能触发存储器再分配任何其他操作后:

// ... 
kid.bar.resize(100); 
kid.invalidate_dtype(); 
// MPI_Send/MPI_Recv 

请原谅以上任何马虎的C++代码。

+0

太好了。如果STL容器存在于struct/class中,这总是要走的路吗?我搜索了人们发送课程的方式,包括STL容器,但找不到任何东西。他们只显示如何单独发送容器。 – Shibli

+0

这只适用于将其元素存储在连续内存中的容器。它不适用于链接列表或集合。对于通过MPI传递C++对象的更通用的方法,您应该查看[boost.MPI](http://www.boost.org/libs/mpi)。它有一个相当通用的序列化机制,支持复杂的数据结构。 –

1

这里的问题是,你告诉MPI从&kid发送整数块,但是这而不是你的数据在哪里。 &kid指向一个std::vector对象,该对象具有指向您在堆上某处分配的整数块的内部指针。

kid.age.data()代替&kid,它应该工作。当您不调整大小时,它“起作用”的原因是矢量将为0大小,因此MPI将尝试发送空信息并且不会发生实际的内存访问。

+0

编译器抱怨“'struct Child'没有名为'data'的成员”。 – Shibli

+0

@Shibli:你需要序列化_'kid.age.data()'_而不是'&kid'。 – ildjarn

+0

@ildjarn是对的,我的意思是'kid.age.data()'。纠正。 – suszterpatt

0

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

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

第二个即使在动态库之间,您也无法发送STL容器,对于应用程序实例也是如此。因为它们可以用不同版本的STL进行编译,并且在不同的架构上有不同的工作方式。

下面是关于这部分的质询不错的答案:https://stackoverflow.com/a/22797419/440168

+1

第二部分不适用于这个问题。 OP定义了一个MPI数据类型,该数据类型映射到内存中连续存储的整数类型的'age.size()'元素序列,这正是'std :: vector '的作用。使用MPI作为中间件,它不仅可以在随机进程(或称为应用程序实例)之间工作,而且可以在不同体系结构(如果MPI实现支持异构环境)的进程之间工作。 –