Проблема с созданием структуры MPI, ошибка 11 при вызове MPI_Bcast

Я хочу транспортировать структуру между процессами, и для этого я пытаюсь создать структуру MPI. Код предназначен для алгоритма оптимизации колонии муравьев (ACO).

Заголовочный файл со структурой C содержит:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <math.h>
    #include <mpi.h>

    /* Constants */
    #define NUM_CITIES 100      // Number of cities
    //among others

    typedef struct {
        int city, next_city, tabu[NUM_CITIES], path[NUM_CITIES], path_index;
        double tour_distance;
    } ACO_Ant;

Я попытался создать свой код, как было предложено в этой теме.

Код программы:

    int main(int argc, char *argv[])
    {
    MPI_Datatype MPI_TABU, MPI_PATH, MPI_ANT;

    // Initialize MPI
    MPI_Init(&argc, &argv);
    //Determines the size (&procs) of the group associated with a communicator (MPI_COMM_WORLD)
    MPI_Comm_size(MPI_COMM_WORLD, &procs);
    //Determines the rank (&rank) of the calling process in the communicator (MPI_COMM_WORLD)
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    MPI_Type_contiguous(NUM_CITIES, MPI_INT, &MPI_TABU);
    MPI_Type_contiguous(NUM_CITIES, MPI_INT, &MPI_PATH);
    MPI_Type_commit(&MPI_TABU);
    MPI_Type_commit(&MPI_PATH);

    // Create ant struct
    //int city, next_city, tabu[NUM_CITIES], path[NUM_CITIES], path_index;
    //double tour_distance;
    int blocklengths[6] = {1,1, NUM_CITIES, NUM_CITIES, 1, 1};
    MPI_Datatype    types[6] = {MPI_INT, MPI_INT, MPI_TABU, MPI_PATH, MPI_INT, MPI_DOUBLE};
    MPI_Aint        offsets[6] = { offsetof( ACO_Ant, city ), offsetof( ACO_Ant, next_city), offsetof( ACO_Ant, tabu), offsetof( ACO_Ant, path ), offsetof( ACO_Ant, path_index ), offsetof( ACO_Ant, tour_distance )};

    MPI_Datatype tmp_type;
    MPI_Aint lb, extent;

    MPI_Type_create_struct(6, blocklengths, offsets, types, &tmp_type);
    MPI_Type_get_extent( tmp_type, &lb, &extent );
    //Tried all of these
    MPI_Type_create_resized( tmp_type, lb, extent, &MPI_ANT );
    //MPI_Type_create_resized( tmp_type, 0, sizeof(MPI_ANT), &MPI_ANT );
    //MPI_Type_create_resized( tmp_type, 0, sizeof(ant), &MPI_ANT );
    MPI_Type_commit(&MPI_ANT);

    printf("Return: %d\n" , MPI_Bcast(ant, NUM_ANTS, MPI_ANT, 0, MPI_COMM_WORLD));
    }

Но как только программа достигает команды MPI_Bcast, происходит сбой с кодом ошибки 11, который, я полагаю, является MPI_ERR_TOPOLOGY согласно этому руководству. — это ошибка сегментации (сигнал 11).

Я также не уверен в некоторых частях кода, почему автор исходной программы. Может ли кто-нибудь объяснить, почему они создают

MPI_Aint displacements[3];
MPI_Datatype typelist[3];

размера 3, когда структура имеет 2 переменные?

int block_lengths[2];

Код:

    void ACO_Build_best(ACO_Best_tour *tour, MPI_Datatype *mpi_type /*out*/)
    {
        int block_lengths[2];
        MPI_Aint displacements[3];
        MPI_Datatype typelist[3];
        MPI_Aint start_address;
        MPI_Aint address;

        block_lengths[0] = 1;
        block_lengths[1] = NUM_CITIES;

        typelist[0] = MPI_DOUBLE;
        typelist[1] = MPI_INT;

        displacements[0] = 0;

        MPI_Address(&(tour->distance), &start_address);
        MPI_Address(tour->path, &address);
        displacements[1] = address - start_address;

        MPI_Type_struct(2, block_lengths, displacements, typelist, mpi_type);
        MPI_Type_commit(mpi_type);
    }

Любая помощь будет оценена по достоинству.
Изменить: помощь в решении проблемы, а не малополезный жаргон StackOverflow


person PhilipTsv    schedule 14.04.2019    source источник
comment
Пожалуйста, включите минимальный, полный и проверяемый пример.   -  person Elias    schedule 14.04.2019
comment
Отредактировано. Добавлен дополнительный код для полноты.   -  person PhilipTsv    schedule 14.04.2019
comment
Либо blocklengths, либо types неверно.   -  person Gilles Gouaillardet    schedule 15.04.2019
comment
11 скорее всего связан с сигналом SIGSEGV   -  person Gilles Gouaillardet    schedule 15.04.2019
comment
Вы правы в обоих случаях. Дополнительную информацию см. в ответе Христо Илиева.   -  person PhilipTsv    schedule 15.04.2019


Ответы (1)


Эта часть неверна:

int blocklengths[6] = {1,1, NUM_CITIES, NUM_CITIES, 1, 1};
MPI_Datatype    types[6] = {MPI_INT, MPI_INT, MPI_TABU, MPI_PATH, MPI_INT, MPI_DOUBLE};
MPI_Aint        offsets[6] = { offsetof( ACO_Ant, city ), offsetof( ACO_Ant, next_city), offsetof( ACO_Ant, tabu), offsetof( ACO_Ant, path ), offsetof( ACO_Ant, path_index ), offsetof( ACO_Ant, tour_distance )};

Типы данных MPI_TABU и MPI_PATH уже охватывают NUM_CITIES элементов. Когда вы укажете, что соответствующий размер блока также равен NUM_CITIES, результирующий тип данных попытается получить доступ к NUM_CITIES * NUM_CITIES элементам, что, вероятно, приведет к segfault (сигнал 11).

Либо установите все элементы blocklengths в 1, либо замените MPI_TABU и MPI_PATH в массиве types на MPI_INT.

Эта часть также неверна:

MPI_Type_create_struct(6, blocklengths, offsets, types, &tmp_type);
MPI_Type_get_extent( tmp_type, &lb, &extent );
//Tried all of these
MPI_Type_create_resized( tmp_type, lb, extent, &MPI_ANT );
//MPI_Type_create_resized( tmp_type, 0, sizeof(MPI_ANT), &MPI_ANT );
//MPI_Type_create_resized( tmp_type, 0, sizeof(ant), &MPI_ANT );
MPI_Type_commit(&MPI_ANT);

Вызов MPI_Type_create_resized со значениями, возвращаемыми MPI_Type_get_extent, бессмысленен, поскольку он просто дублирует тип без фактического изменения его размера. Использование sizeof(MPI_ANT) неверно, так как MPI_ANT - это не тип C, а дескриптор MPI, который является либо целочисленным индексом, либо указателем (зависит от реализации). Он будет работать с sizeof(ant), если ant имеет тип ACO_Ant, но если вы вызываете MPI_Bcast(ant, NUM_ANTS, ...), то ant является либо указателем, и в этом случае sizeof(ant) является просто размером указателя, либо массивом, и в этом случае sizeof(ant) в NUM_ANTS раз больше, чем Это должно быть. Правильный вызов:

MPI_Type_create_resized(tmp_type, 0, sizeof(ACO_Ant), &ant_type);
MPI_Type_commit(&ant_type);

И, пожалуйста, никогда не используйте MPI_ в качестве префикса в ваших собственных именах переменных или функций. Это делает код нечитаемым и вводит в заблуждение («это предопределенный тип данных MPI или пользовательский?»)

Что касается последнего вопроса, то автор мог иметь в виду другую структуру. Ничто не мешает вам использовать большие массивы, если вы вызываете MPI_Type_create с правильным количеством значимых элементов.

Примечание. Вам не нужно фиксировать типы данных MPI, которые никогда не используются напрямую в коммуникационных вызовах. То есть эти две строки не нужны:

MPI_Type_commit(&MPI_TABU);
MPI_Type_commit(&MPI_PATH);
person Hristo Iliev    schedule 15.04.2019
comment
Спасибо за подробный ответ! Я отредактировал код, как вы предложили, и он работает. Обратите внимание, что я взял стиль MPI_(name) от оригинального автора, так как я хотел, чтобы мой код был совместим с его - я использую некоторые из тех же функций и т. д., а также потому, что это мой первый проект MPI. Однако, как вы сказали, временами это сбивало меня с толку. И да, я не ожидал, что MPI_Type_create_resized(tmp_type, 0, sizeof(MPI_ANT), &MPI_ANT); работать, так как тип mpi в этот момент пуст, но я все равно попробовал, так как был уверен, что код до этого правильный (очевидно, нет). - person PhilipTsv; 15.04.2019