2016-10-16 67 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實現支持異構環境)的進程之間工作。 –