【问题标题】:MPI-IO write to file in non contiguous patternMPI-IO 以非连续模式写入文件
【发布时间】:2019-08-09 16:20:46
【问题描述】:

我在编写将以特定模式编写的并行 MPI I/O 程序时遇到问题。 我能够让进程 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,但无法正常工作。有人可以指导我正确的方向吗?

【问题讨论】:

  • 效率注意事项:使用步幅永远是一个杀手!这实际上取决于您使用的进程数量、连续块的大小、文件的大小和磁盘的数量……如果您的文件只有几 Gb,并且集群上有 200 个条带,那就没问题了。 ..但是有一个几Tb的文件,使用顺序> 10k进程,最好但实际上最好的总是写你的文件,每个进程都在你的文件中连续写入。根据经验,您可能会获得 10 倍的 I/O 带宽...

标签: mpi mpi-io


【解决方案1】:

正如您所了解的,您需要设置一个视图...

然后你的代码中有一些小错误: 1)每个进程真的需要超过 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*size 的步幅编写 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;
}

【讨论】:

  • Oups 我在写完之前提交它......所以现在它完成了:-)
  • 如果性能很重要,您应该真正使用集体 I/O。这里不需要mtype,所以写命令可以是MPI_File_write_all(fhw, buf, N, MPI_INT, &amp;status);
  • 我不确定,如果你有 2 个对象是的,但如果它变成一个大数字,将块分组不是更好吗?那么确实 write_all 会更好
  • 我希望 MPI 实现能很好地处理这个问题,所以我宁愿让用户代码尽可能简单。
【解决方案2】:

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 超出了我们的需要
  • VECTOR.. 现在我们到了某个地方。

在 C 中,VECTOR 的原型是这样的:

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

在此示例中,您希望每个进程编写一个包含 5 个块的向量。每个块有两个元素。每个块开始之间的步幅是 2*nprocs 或 8。'oldtype' 是 MPI_INT。

您已经在使用 MPI_FILE_WRITE_AT。也许考虑 MPI_FILE_WRITE_AT_ALL 以利用库可能提供的任何集体 i/o 优化。

【讨论】:

  • 不错的答案,但我认为只有当你知道怎么做才会有帮助:-)
猜你喜欢
  • 1970-01-01
  • 2015-03-13
  • 2013-07-29
  • 2015-09-12
  • 1970-01-01
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 2012-04-14
相关资源
最近更新 更多