Ошибка при чтении и записи тестового файла в простом коде MPI

#c #mpi

#c #mpi

Вопрос:

У меня есть код MPI, в котором процессы считывают двоичный файл и записывают его обратно. Способ распределения данных заключается в том, что процесс 0 считывает (а затем записывает) первую половину файла, тогда как процесс 1 считывает (а затем записывает) вторую половину файла. Проблема здесь в том, что входные и выходные файлы не совпадают (diff показывает, что они отличаются). Если есть только 1 процесс, все работает нормально. Может кто-нибудь указать, что происходит не так?

Используя OpenMPI, скомпилированный как: mpicc -Wall test_mpi.c -o test_mpi
Выполняется как: mpirun -np 2 ./test_mpi

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

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

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

    int rank, np, i;  //np = no. of processes
    MPI_Init(amp;argc, amp;argv);
    MPI_Comm_rank(MPI_COMM_WORLD, amp;rank);
    MPI_Comm_size(MPI_COMM_WORLD, amp;np);

    int filesize = 48*1048576;  //input filesize 48MB

    double *data = (double*) malloc (filesize/np);
    FILE* fpa;
    fpa = fopen ( "512_featurevec.out", "rb");
    fseek(fpa, filesize/np*rank, SEEK_SET);
    printf("read: %dn", (int)fread(amp;data[0], sizeof(double), filesize/(np*sizeof(double)), fpa));
    fclose(fpa);

    char* outfile = "outfile.txt";
    for(i=0; i<np; i  ) {
        if(rank == i) {
            fpa = fopen ( outfile, "ab");
            fseek(fpa, filesize/np*rank, SEEK_SET);
            fwrite ( amp;data[0], sizeof(double), filesize/(np*sizeof(double)), fpa);
            fclose ( fpa );
        }   
    }   

    free(data);
    MPI_Finalize();
    exit(0);
}
  

Комментарии:

1. Пожалуйста, не приводите указатель, возвращаемый malloc() . Вам вообще не нужно его трогать, и, если вы это сделаете, вы можете допустить ошибки.

Ответ №1:

Похоже, проблема связана с тем, что каждый дочерний элемент открывает файл для записи, что приводит к конфликту.

Попробуйте, чтобы имя файла зависело от ранга (например, запись в out file.txt.(rank) и посмотрите, совпадают ли все выходные данные.

Комментарии:

1. Спасибо за этот пост, я ломал голову, не добиваясь никакого прогресса, пока не прочитал этот пост. Так просто.

Ответ №2:

Если вы уже используете MPI и сталкиваетесь с проблемой использования seek для разделения файла, вместо использования POSIX я бы предложил использовать MPI-IO (стандарт как часть MPI2, c. 1996 или около того): Хорошие ссылки:

и в нашем центре у нас есть первая часть этого, которая, я думаю, довольно хороша:

Версия вашего кода с поддержкой MPI, приведенная выше, такова:

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

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

    int rank, np;
    MPI_Init(amp;argc, amp;argv);
    MPI_Comm_rank(MPI_COMM_WORLD, amp;rank);
    MPI_Comm_size(MPI_COMM_WORLD, amp;np);

    const int filesize = 48*1048576;  //input filesize 48MB
    const int ndoubles = filesize/(sizeof(double)*np);
    double *localdata = malloc(ndoubles*sizeof(double));

    /* create a type which describes our view of the file --
     * in particular, just our subarray of the global array
     */
    int globalsizes[1] = {filesize};
    int localsizes[1] = {ndoubles};
    int starts[1] = {ndoubles*rank};

    MPI_Datatype fileview;
    MPI_Type_create_subarray(1, globalsizes, localsizes, starts, MPI_ORDER_C, MPI_DOUBLE, amp;fileview);
    MPI_Type_commit(amp;fileview);

    /* read in only our data */
    MPI_File fpa;
    MPI_Status status;
    MPI_File_open(MPI_COMM_WORLD, "512_featurevec.out", MPI_MODE_RDONLY, MPI_INFO_NULL, amp;fpa);

    /* note could use MPI_File_seek instead of file set view */
    MPI_File_set_view(fpa, (MPI_Offset)0, MPI_DOUBLE, fileview, "native", MPI_INFO_NULL);
    MPI_File_read_all(fpa, localdata, ndoubles, MPI_DOUBLE, amp;status);
    MPI_File_close(amp;fpa);

    /* write out data - it will have same layout, we're just writing instead of erading*/

    MPI_File_open(MPI_COMM_WORLD, "output.dat", MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, amp;fpa);

    /* note could use MPI_File_seek instead of file set view */
    MPI_File_set_view(fpa, (MPI_Offset)0, MPI_DOUBLE, fileview, "native", MPI_INFO_NULL);
    MPI_File_write_all(fpa, localdata, ndoubles, MPI_DOUBLE, amp;status);
    MPI_File_close(amp;fpa);

    free(localdata);
    MPI_Type_free(amp;fileview);
    MPI_Finalize();

    return 0;
}
  

Комментарии:

1. Я быстро захотел измерить ускорение ввода-вывода, поэтому я попытался распараллелить fwrite, как я показал. Но я согласен, MPI_IO определенно лучший способ.

2. Вы можете использовать MPI_File_seek (или MPI_File_write_at) очень похожим образом на то, что у вас уже есть, и у него будет лучшая семантика (например, не будет странно действовать, если кто-то другой переместит указатель на файл …)

Ответ №3:

Отвечая на мой собственный вопрос, но это работает:

 char* outfile = "outfile.txt";
    for(i=0; i<np; i  ) {
        if(rank == i) {
            fpa = fopen ( outfile, "ab");
            fseek(fpa, filesize/np*rank, SEEK_SET);
            fwrite ( amp;data[0], sizeof(double), filesize/(np*sizeof(double)), fpa);
            fclose ( fpa );
        } 
        MPI_Barrier(comm);  
    } 
  

По какой-то причине, если proc 1 прибывает перед proc 0 и fseeks пустой файл с некоторым значением, он помещает указатель файла на правильное смещение (проверено ftell), но записывает только со смещения 0. (Должно быть, я делаю что-то тривиально неправильное, но в любом случае).