Проблема синхронизации общей памяти MPI - PullRequest
1 голос
/ 06 июня 2019

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

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

У меня две проблемы:

  1. Синхронизация (пассивная цель, эпоха блокировки / разблокировки) чрезвычайно медленная.
  2. Результат показывает, что внутри эпох есть некоторая несогласованность (блокировка / разблокировка блоков). Очевидно, что есть проблема состояния гонки.

Я пробовал с активной синхронизацией цели (MPI_Win_Fence ()), но возникают те же проблемы. Поскольку у меня нет большого опыта с этим, возможно, я просто использую неправильный подход.

MPI_Comm nodecomm;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
    MPI_INFO_NULL, &nodecomm);

MPI_Comm_size(nodecomm, &nodesize);
MPI_Comm_rank(nodecomm, &noderank);

int local_xx_size = 0;
if (noderank == 0){
    local_xx_size = xx_size;
}

MPI_Win win_xx;
MPI_Aint winsize;    
double *xx, *local_xx;    
MPI_Win_allocate_shared(local_xx_size*sizeof(double), sizeof(double),
    MPI_INFO_NULL, nodecomm, &local_xx, &win_xx);

xx = local_xx;
if (noderank != 0){
    MPI_Win_shared_query(win_xx, 0, &winsize, &windisp, &xx);
}

//init xx
if(noderank == 0){
    MPI_Win_lock_all(0, win_xx);
    for (i=0; i<xx_size; i++){
        xx[i]=0.0;
    }
    MPI_Win_unlock_all(win_xx);
}
MPI_Barrier(nodecomm);

long counter = 0;
for(i = 0; i < largeNum; i++) {
    //some calculations
    for(j = 0; j < xx_size; j++) {
        //calculate res
        MPI_Win_lock_all(0, win_xx);
        xx[counter] += res; //update value
        MPI_Win_unlock_all(win_xx);
    }
}
MPI_Barrier(nodecomm);

//use xx (sync data from all the nodes)
MPI_Win_free(&win_xx);

Буду признателен за любую помощь и предложение относительно этих проблем.

1 Ответ

0 голосов
/ 07 июня 2019

Краткое объяснение

Блокировка / разблокировка MPI сами по себе не вызывают атомных обновлений.

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

Вы можете получить атомарность, используя функции накопления MPI (Accumulate, Get_accumulate, Fetch_and_op, Compare_and_swap) или - и только в случае разделяемой памяти - вы можете использовать атомарные примитивы, связанные с вашим компилятором. Поскольку это немного сложно с C11 / C ++ 11, потому что они требуют типов, я покажу пример ниже со встроенными функциями, которые допускаются большинством, если не всеми распространенными компиляторами.

Предложение по изменению кода

Я не знаю, правильно ли это. Это просто демонстрирует концепции, отмеченные выше.

MPI_Comm nodecomm;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
    MPI_INFO_NULL, &nodecomm);

MPI_Comm_size(nodecomm, &nodesize);
MPI_Comm_rank(nodecomm, &noderank);

int local_xx_size = 0;
if (noderank == 0){
    local_xx_size = xx_size;
}

MPI_Win win_xx;
MPI_Aint winsize;    
double *xx, *local_xx;    
MPI_Win_allocate_shared(local_xx_size*sizeof(double), sizeof(double), MPI_INFO_NULL, nodecomm, &local_xx, &win_xx);
MPI_Win_lock_all(0, win_xx);

xx = local_xx;
if (noderank != 0){
    MPI_Win_shared_query(win_xx, 0, &winsize, &windisp, &xx);
}

//init xx
if(noderank == 0){

    for (i=0; i<xx_size; i++){
        xx[i]=0.0;
    }
}
MPI_Barrier(nodecomm);

long counter = 0;
for(i = 0; i < largeNum; i++) {
    //some calculations
    for(j = 0; j < xx_size; j++) {
        //calculate res
        // xx[counter] += res; //update value
#ifdef USE_RMA_ATOMICS
        // check the arguments - I don't know if I calculate the target+displacement right
        int target = counter/local_xx_size;
        MPI_Aint disp = counter%local_xx_size;
        MPI_Accumulate(&res, MPI_LONG, target, disp, 1, MPI_LONG, MPI_SUM, win_xx);
        MPI_Win_flush(target, win_xx);
#else
# ifdef USE_NEWER_INTRINSICS // GCC, Clang, Intel support this AFAIK
        __atomic_fetch_add (&xx[counter], res, __ATOMIC_RELAXED);
# else // GCC, Clang, Intel, IBM support this AFAIK
        __sync_fetch_and_add(&xx[counter], res);
# endof
#endif
    }
}
MPI_Barrier(nodecomm);

MPI_Win_unlock_all(win_xx);
MPI_Win_free(&win_xx);
...