проблема заказа в синхронизации MPI-2 односторонней связи

Я изучаю MPI-2 и пытаюсь реализовать первую простую функциональность, используя одностороннюю связь MPI-2:

У процесса 0 хост один массив фиксированного размера data_size,

Каждый процесс (включая 0) будет генерировать массив и сравнивать с массивом хоста:

Если первый элемент сгенерированного массива меньше, чем у массива хоста, замените массив хоста сгенерированным.

В коде:

vector<int> v1 = {rank,rank+1,rank+2};
v = get_vec(vec);
if (v1[0] < v[0])
put_vec(vec,v1);

Полный код внизу. Конечно, я ожидаю, что из всех сгенерированных массивов тот, у которого наименьший элемент заголовка должен быть в конечном итоге в массиве хоста после завершения программы, поскольку наименьший массив ([0,1,2] в этом примере) заменит другие и не заменится сам.

Тем не менее, в некоторых (редких) случаях я получал вывод, подобный этому:

$ mpiexec.exe -n 4 a.exe
#0 assigns v1 {0 ...} to host v {2 ...}
#1 assigns v1 {1 ...} to host v {2 ...}
1  2  3

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

Кто-нибудь может объяснить, как мне исправить мой код, чтобы получить ожидаемый результат?

Заранее спасибо.


Полный код скомпилирован с использованием g++ -std=c++11 test.cpp -lmpi:

#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#include <chrono>

#include <iostream>
#include <vector>

using namespace std;struct mpi_vector_t {
MPI_Win win;
int  hostrank;  //id of the process that host values to be exposed to all processes
int  rank;    //process id
int  size;     //number of processes
int  *data;
int  data_size;
};

struct mpi_vector_t *create_vec(int hostrank, std::vector<int> v) {
struct mpi_vector_t *vec;

vec = (struct mpi_vector_t *)malloc(sizeof(struct mpi_vector_t));
vec->hostrank = hostrank;
vec->data_size = v.size();
MPI_Comm_rank(MPI_COMM_WORLD, &(vec->rank));
MPI_Comm_size(MPI_COMM_WORLD, &(vec->size));

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

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

return;
}

std::vector<int> get_vec(struct mpi_vector_t *vec) {
vector<int> ret(vec->data_size);
MPI_Win_lock(MPI_LOCK_SHARED, vec->hostrank, 0, vec->win);
MPI_Get(&ret.front(), vec->data_size, MPI_INT, vec->hostrank, 0, vec->data_size, MPI_INT, vec->win);
MPI_Win_unlock(0, vec->win);
return ret;
}

void put_vec(struct mpi_vector_t *vec, std::vector<int> v) {
MPI_Win_lock(MPI_LOCK_EXCLUSIVE, vec->hostrank, 0, vec->win);
MPI_Put(&v.front(), vec->data_size, MPI_INT, vec->hostrank, 0, vec->data_size, MPI_INT, vec->win);
MPI_Win_unlock(0, vec->win);
}

void print_vec(struct mpi_vector_t *vec) {
if (vec->rank == vec->hostrank) {
for (int i=0; i<vec->data_size; i++) {
printf("%2d ", vec->data[i]);
}
puts("");
}
}int main(int argc, char **argv) {

MPI_Init(&argc, &argv);

struct mpi_vector_t *vec;
int rank;

vector<int> v = {2,3,1};
vec = create_vec(0, v);

MPI_Comm_rank(MPI_COMM_WORLD, &rank);

for (int itest = 0; itest < 2; itest++) {
vector<int> v1 = { rank, rank + 1, rank + 2 }; //some generated data
v = get_vec(vec);
if (v1[0] < v[0]) {
cout << "#" << rank << " assigns v1 {" << v1[0] <<
" ...} to host v {" << v[0] << " ...}" << endl;
put_vec(vec, v1);
}
}

MPI_Barrier(MPI_COMM_WORLD);
print_vec(vec);
delete_vec(&vec);

MPI_Finalize();
return 0;
}

1

Решение

Это классический сценарий гонки данных. И то и другое get_vec а также put_vec заблокируйте окно индивидуально, но на самом деле вам нужна блокировка, охватывающая весь блок кода, т.е.

lock_window();
v = get_vec(vec);
if (v1[0] < v[0])
put_vec(vec,v1);
unlock_window();

В настоящее время возможно, что содержимое общего вектора может измениться сразу после вызова get_vec() потому что другой процесс выполнен put_vec() и это может сделать недействительным результат сравнения. Что-то вроде этого:

std::vector<int> compare_swap_vec(struct mpi_vector_t *vec, std::vector v) {
vector<int> ret(vec->data_size);
MPI_Win_lock(MPI_LOCK_EXCLUSIVE, vec->hostrank, 0, vec->win_ext);
ret = get_vec(vec);
if (v[0] < ret[0])
put_vec(vec, v);
MPI_Win_unlock(0, vec->win_ext);
return ret;
}

Функция compare_swap_vec() берет вектор и использует его для замены старого содержимого общего вектора, если выполняется отношение меньше. Он также возвращает предыдущее содержимое вектора. vec->win_ext это другое окно, размещаемое тем же процессом, в котором размещается векторный контент. Он используется для внешней блокировки, поскольку стандарт MPI требует, чтобы разные эпохи доступа для одного и того же окна в одном и том же процессе были непересекающимися, что я интерпретирую, поскольку вложенные блокировки в одном и том же окне не допускаются.

2

Другие решения


По вопросам рекламы [email protected]