Запись MPI-IO в файл в несмежном порядке - PullRequest
3 голосов
/ 19 марта 2019

У меня проблемы с написанием параллельной программы ввода / вывода MPI, которая будет писать по определенному шаблону. Я смог получить процесс 0 записать целые числа 0-9, процесс 1 записать целые числа 10-19, процесс 2 записать целые числа 20-29 и т. Д.

proc 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
proc 1: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
proc 2: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
proc 3: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

Вот код, который выполняет это:

int main(int argc, char *argv[]) {
    // MPI_Finalize();

    int i, rank, size, offset;
    MPI_File fhw;
    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    int N = size * 10;
    int buf[N];
    for ( i = 0; i < N; ++i ) {
        buf[i] = rank * 10 + i;
    }

    offset = rank * (N/size) * sizeof(int);
    MPI_File_open(MPI_COMM_WORLD, "datafile", MPI_MODE_CREATE|MPI_MODE_WRONLY,
                MPI_INFO_NULL, &fhw);
    printf("(%d) Writing to file...\n", rank);
    printf("\nRank: (%d), Offset: %d\n", rank, offset);
    MPI_File_write_at(fhw, offset, buf, (N/size), MPI_INT, &status);
    MPI_File_close(&fhw);

    MPI_Finalize();

    return 0;
}

Однако я не совсем понимаю, как мне получить следующий результат:

// starting out:
proc 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
proc 1: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
proc 2: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
proc 3: [30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
// proc 0 writes first 2 slots, then proc 1 writes next 2, etc.
result: [0, 1, 10, 11, 20, 21, 30, 31, 2, 3, 12, 13, 22, 23, ..., 8, 9, 18, 19, 28, 29, 38, 29]

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

Ответы [ 2 ]

2 голосов
/ 20 марта 2019

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

Тогда небольшая ошибка в вашем коде: 1) Действительно ли вам нужен buf более чем на 10 номеров для каждого процесса?2) Смещение в MPI_File_wite_at - это примечание в байтах, но по номеру элемента (относительно размера элемента вашего представления)

Таким образом, чтобы настроить представление, вам нужна только 1 строка:

#include "mpi.h"
#include <cstdio>

int main(int argc, char *argv[]) {
    // MPI_Finalize();                                                                                                      

    int i, rank, size, offset;
    MPI_File fhw;
    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    int N = 10; 
    int buf[N];
    for ( i = 0; i < N; ++i ) {
        buf[i] = rank * 10 + i;
    }

    offset = 10*rank;
    MPI_File_open(MPI_COMM_WORLD,"datafile",MPI_MODE_CREATE|MPI_MODE_WRONLY,
                MPI_INFO_NULL, &fhw);
    printf("(%d) Writing to file...\n", rank);
    printf("\nRank: (%d), Offset: %d\n", rank, offset);
    MPI_File_set_view( fhw,0,MPI_INT, MPI_INT, "native", MPI_INFO_NULL ) ;
    MPI_File_write_at(fhw, offset, buf, N, MPI_INT, &status);
    MPI_File_close(&fhw);

    MPI_Finalize();

    return 0;
}

Затем вы можете сделать то же самое, используя MPI_File_write :-) и по-разному настраивая представление для каждого процесса, просто замените представление и напишите:

MPI_File_set_view(fhw,offset*sizeof(int),MPI_INT,MPI_INT,
                        "native",MPI_INFO_NULL ) ;
MPI_File_write_at(fhw, 0, buf, N, MPI_INT, &status);

или просто:

MPI_File_set_view(fhw,offset*sizeof(int),MPI_INT,MPI_INT,
                        "native",MPI_INFO_NULL ) ;
MPI_File_write(fhw, buf, N, MPI_INT, &status);

ПРИМЕЧАНИЕ: в представлении смещение указывается в байтах, а при записи - в размере элементов представления .... Может быть немного запутанно: -)

Тогда магия:

Вам нужно написать блоки по 2 дюйма с шагом 2 *, и у вас есть N / 2 из этих блоков, поэтому вы создаете тип:

MPI_Type_vector(N/2, 2 , size*2,  MPI_INT, &ftype);
MPI_Type_commit(&ftype);

и задаете вид:

MPI_File_set_view( fhw, rank*2*sizeof(int), MPI_INT, ftype, "native", MPI_INFO_NULL ) ;

тогда вы должны думать, что в памяти у вас есть постоянное хранилище ваших данных, чтобы соответствовать вашему представлению, оно должно быть чем-то вроде N / 2 блоков, поэтому вы создаете тип данных:

MPI_Type_contiguous(2,   MPI_INT, &mtype);
MPI_Type_commit(&mtype);

Тогда вы готовы к записи:

MPI_File_write(fhw, buf, N/2, mtype, &status);
MPI_File_close(&fhw);

И так весь код станет:

#include "mpi.h"
#include <cstdio>

int main(int argc, char *argv[]) {
    int i, rank, size, offset;
    MPI_File fhw;
    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    int N = 10; //need to be even!!!
    int buf[N];
    for ( i = 0; i < N; ++i ) {
        buf[i] = rank * N + i;
    }

    offset = 10*rank;
    MPI_File_open(MPI_COMM_WORLD, "datafile", MPI_MODE_CREATE|MPI_MODE_WRONLY,
                    MPI_INFO_NULL, &fhw);
    printf("(%d) Writing to file...\n", rank);
    printf("\nRank: (%d), Offset: %d\n", rank, offset);

    MPI_Datatype ftype,mtype;
    MPI_Type_vector(N/2, 2 , size*2,  MPI_INT, &ftype);
    MPI_Type_commit(&ftype);

    MPI_File_set_view( fhw, rank*2*sizeof(int), MPI_INT, ftype,
                         "native",MPI_INFO_NULL ) ;

    MPI_Type_contiguous(2,   MPI_INT, &mtype);
    MPI_Type_commit(&mtype);

    MPI_File_write(fhw, buf, N/2, mtype, &status);
    MPI_File_close(&fhw);

    MPI_Finalize();
    return 0;
}
1 голос
/ 19 марта 2019

MPI-файлы немного сложнее, конечно. Разбейте процесс на процесс:

rank 0:  0 1 - - - - - - 2 3 - - - - - - 4 5
rank 1:  - - 10 11 - - - - - - 12 13 - - - - - -
rank 2:  - - - - 20 21 - - - - - -
rank 3:  - - - - - - 30 31 - - - - - - 

Вы можете выбирать из множества типов данных MPI. Какой из них подходит?

  • При достаточной работе самый общий тип STRUCT может выражать что угодно, но вы работаете исключительно с MPI_INT, поэтому STRUCT излишне
  • Вы можете перечислить смещения и блок-линзы с типом INDEXED, но ваш шаблон регулярен: каждый блок состоит из двух целых чисел
  • Поэтому вы можете использовать BLOCKINDEXED. Однако интервал между блоками также является регулярным, что делает BLOCKINDEXED больше, чем нам нужно
  • ВЕКТОР .. теперь мы куда-то добираемся.

В C, прототип VECTOR это:

int MPI_Type_vector(int count, int blocklength, int stride,
                    MPI_Datatype oldtype, MPI_Datatype *newtype)

В этом примере вы хотите, чтобы каждый процесс записывал вектор из 5 блоков. Каждый блок имеет два элемента. Шаг между началом каждого блока равен 2 * nprocs или 8. Старый тип - MPI_INT.

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

...