2016-10-16 3 views
1

Почему я получаю следующую ошибку для следующего кода с помощью команды mpirun -np 2 ./out? Я позвонил make_layout() после изменения размера std::vector, так что обычно я не должен получать эту ошибку. Он работает, если я не изменяю размер. Какова причина?Ошибка сегментации при отправке структуры с std :: vector member

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

Это работает только для контейнеров, которые хранят свои элементы в непрерывной памяти. Он не будет работать со связанными списками или наборами. Для более общего способа передачи объектов C++ с MPI вы должны изучить [boost.MPI] (http://www.boost.org/libs/mpi). Он имеет довольно общий механизм сериализации, который поддерживает сложные структуры данных. –

1

Проблема здесь состоит в том, что вы говорите, MPI, чтобы отправить блок целых чисел от &kid, но это а не где ваши данные. &kid указывает на объект std::vector, который имеет внутренний указатель на ваш блок целых чисел, выделенный где-то в куче.

Замените &kid на kid.age.data() и он должен работать. Причина, по которой он «работает», когда вы не изменяете размер, состоит в том, что векторы будут иметь размер 0, поэтому MPI попытается отправить пустое сообщение и фактический доступ к памяти не произойдет.

+0

компилятор жалуется, что "'struct Child' не имеет имени с именем 'data'. – Shibli

+0

@Shibli: Вам нужно сериализовать _'kid.age.data() '_, а не' & kid'. – ildjarn

+0

@ildjarn прав, я имел в виду 'kid.age.data()'. Исправленный. – suszterpatt

0

Будьте осторожны, вы столкнулись с несколькими проблемами.

Первыеstd::vector хранит объект в куче, поэтому данные на самом деле не хранятся внутри структуры.

Second Вы не можете отправлять контейнеры STL даже между динамическими библиотеками, также для экземпляров приложений это также верно. Потому что они могут быть скомпилированы с различными версиями STL и работать по разным архитектурам по-разному.

Вот хороший ответ об этой части вопроса: https://stackoverflow.com/a/22797419/440168

+1

Вторая часть не относится к этому вопросу. OP определяет тип данных MPI, который сопоставляется с последовательностью элементов типа age.size() целочисленного типа, хранящихся последовательно в памяти, что является именно тем, что является «std :: vector ». С MPI в качестве промежуточного программного обеспечения он будет работать не только между случайными процессами (или экземплярами приложения, как вы его называете), но также между процессами на разных архитектурах (если реализация MPI поддерживает гетерогенные среды). –

Смежные вопросы