MPI2 / MPI3: MPI_allgather против односторонней связи MPI с учетом синхронизации

Я пишу программу оптимизации с использованием MPI-2, в которой мне нужно иметь std::vector равной длины std::vectors (концептуально), разделяемую всеми процессами. Этот вектор содержит лучшие k решения проблемы, найденной в настоящее время, и обновляется каждый раз, когда одно из множества процессов MPI находит новое лучшее решение. Время, затрачиваемое каждым процессом на поиск нового решения, обычно сильно различается.

У меня вопрос, учитывая проблемы с производительностью при синхронизации и ожидании, следует ли мне использовать коллективы MPI, такие как MPI_allgather, каждый раз, когда будет найдено новое лучшее решение; или мне следует использовать одностороннюю связь в MPI-2, чтобы поддерживать «общий» вектор для всех процессов.

В частности, если я использую MPI_allgather, будут ли процессы, завершающие свою работу, преждевременно бездействовать и ждать какой-либо синхронизации с другими процессами?

У меня есть некоторый опыт работы с двухточечной связью MPI (UPD: а также UPC), но я не использовал коллективные или односторонние коммуникации в реальном кодировании. Я искал SO и нашел соответствующие вопросы / ответы о MPI_allgathers, например. Распространяйте структуру с помощью MPI_Allgather и об одностороннем общении Создание счетчика, который остается синхронизированным между процессами MPI. Но мне трудно сказать точную разницу между этими двумя подходами.

Спасибо,

--- Обновлять ---

В частности, у меня есть пример кода внизу из Создание счетчика, который остается синхронизированным между процессами MPI, который использует односторонний поддерживать единый int "общий". Я попытался адаптировать его для работы с универсальным типом, но не знаю, как заставить его работать, так как у меня проблемы с пониманием исходного кода и того, почему он поддерживает массив data, и как я могу обобщить MPI_Accumulate на пользовательскую функцию (например, просто заменив старый вектор новым).

template // примечание: T могут быть только примитивными типами (не указателями, ссылками или структурами), такими как int и double. struct mpi_array {typedef std :: vector Вектор; MPI_Win win; int hostrank;
int rank;
int size;
Vector val;
Vector * hostvals; };


Код одностороннего счетчика связи:

#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

struct mpi_counter_t {
    MPI_Win win;
    int  hostrank ;
    int  myval;
    int *data;
    int rank, size;
};

struct mpi_counter_t *create_counter(int hostrank) {
    struct mpi_counter_t *count;

    count = (struct mpi_counter_t *)malloc(sizeof(struct mpi_counter_t));
    count->hostrank = hostrank;
    MPI_Comm_rank(MPI_COMM_WORLD, &(count->rank));
    MPI_Comm_size(MPI_COMM_WORLD, &(count->size));

    if (count->rank == hostrank) {
        MPI_Alloc_mem(count->size * sizeof(int), MPI_INFO_NULL, &(count->data));
        for (int i=0; i<count->size; i++) count->data[i] = 0;
        MPI_Win_create(count->data, count->size * sizeof(int), sizeof(int),
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    } else {
        count->data = NULL;
        MPI_Win_create(count->data, 0, 1,
                       MPI_INFO_NULL, MPI_COMM_WORLD, &(count->win));
    }
    count -> myval = 0;

    return count;
}

int increment_counter(struct mpi_counter_t *count, int increment) {
    int *vals = (int *)malloc( count->size * sizeof(int) );
    int val;

    MPI_Win_lock(MPI_LOCK_EXCLUSIVE, count->hostrank, 0, count->win);

    for (int i=0; i<count->size; i++) {

        if (i == count->rank) {
            MPI_Accumulate(&increment, 1, MPI_INT, 0, i, 1, MPI_INT, MPI_SUM,
                           count->win);
        } else {
            MPI_Get(&vals[i], 1, MPI_INT, 0, i, 1, MPI_INT, count->win);
        }
    }

    MPI_Win_unlock(0, count->win);
    count->myval += increment;

    vals[count->rank] = count->myval;
    val = 0;
    for (int i=0; i<count->size; i++)
        val += vals[i];

    free(vals);
    return val;
}

void delete_counter(struct mpi_counter_t **count) {
    if ((*count)->rank == (*count)->hostrank) {
        MPI_Free_mem((*count)->data);
    }
    MPI_Win_free(&((*count)->win));
    free((*count));
    *count = NULL;

    return;
}

void print_counter(struct mpi_counter_t *count) {
    if (count->rank == count->hostrank) {
        for (int i=0; i<count->size; i++) {
            printf("%2d ", count->data[i]);
        }
        puts("");
    }
}

int test1() {
    struct mpi_counter_t *c;
    int rank;
    int result;

    c = create_counter(0);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    result = increment_counter(c, 1);
    printf("%d got counter %d\n", rank, result);

    MPI_Barrier(MPI_COMM_WORLD);
    print_counter(c);
    delete_counter(&c);
}


int test2() {
    const int WORKITEMS=50;

    struct mpi_counter_t *c;
    int rank;
    int result = 0;

    c = create_counter(0);

    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    srandom(rank);

    while (result < WORKITEMS) {
        result = increment_counter(c, 1);
        if (result <= WORKITEMS) {
             printf("%d working on item %d...\n", rank, result);
             sleep(random() % 10);
         } else {
             printf("%d done\n", rank);
         }
    }

    MPI_Barrier(MPI_COMM_WORLD);
    print_counter(c);
    delete_counter(&c);
}

int main(int argc, char **argv) {

    MPI_Init(&argc, &argv);

    test1();
    test2();

    MPI_Finalize();
}

person tinlyx    schedule 11.07.2014    source источник
comment
Вы не можете обобщить MPI_Accumulate на произвольные (то есть не встроенные) типы, потому что это не поддерживается MPI-3 (он часто называется активными сообщениями, для которых вы можете попробовать GASNet).   -  person Jeff Hammond    schedule 10.12.2014


Ответы (1)


Вы обеспокоены тем, что некоторые процессы могут войти в MPI_ALLGATHER раньше других, но это всегда так в любом приложении с синхронизацией, а не только в тех, которые явно используют коллективную связь.

Однако, похоже, вы неправильно понимаете, что делают односторонние операции. Они не предоставляют модель параллельного глобального адресного пространства (PGAS), в которой все синхронизируется за вас. Вместо этого они просто дают вам возможность напрямую обращаться к памяти удаленных процессов. Память каждого процесса по-прежнему отделена. Кроме того, если вы собираетесь переходить от точки к точке до остальной части MPI, я бы не ограничился только функциями MPI-2. В MPI-3 есть некоторые новые вещи, которые также улучшают как коллективные, так и односторонние (особенно последние).

При этом, если вы никогда не использовали ничего, кроме двухточечной, односторонняя работа станет для вас большим скачком. Возможно, вы захотите сделать еще один промежуточный шаг и в любом случае сначала проверить коллективы. Если вы все еще недовольны своей производительностью, вы можете взглянуть на одностороннюю главу, но она очень сложна, и большинство людей обычно в конечном итоге используют что-то, что находится поверх одностороннего, вместо того, чтобы использовать это напрямую (например, возможно, некоторые из языков PGAS).

person Wesley Bland    schedule 11.07.2014
comment
Хотя я согласен с большей частью того, что вы сказали, я не думаю, что использование MPI RMA в этом случае очень сложно. Просто запереть окно, получить к нему доступ и затем отпереть. Проблема состоит в том, чтобы найти реализацию, в которой это могло бы происходить асинхронно в процессе, в котором размещено окно. По крайней мере, с Open MPI компонент RMA по умолчанию принимает только удаленные операции всякий раз, когда хост-процесс обращается к MPI, в основном создавая точку синхронизации, которая разрушает всю идею использования RMA. - person Hristo Iliev; 12.07.2014
comment
Верно. Чтобы сделать это эффективно, вам нужно использовать некоторые из пассивных целевых RMA, которые были добавлены в MPI-3, и это совсем другое дело. - person Wesley Bland; 12.07.2014
comment
Итак, есть ли какие-нибудь примеры кода, делающие то, что близко к тому, что я хочу делать в OP? Спасибо. - person tinlyx; 12.07.2014