【问题标题】:Send dynamic array with dynamic size using MPI_Bcast使用 MPI_Bcast 发送具有动态大小的动态数组
【发布时间】:2015-03-11 17:01:00
【问题描述】:

OpenMPI:我想读取根节点上的文件并将该文件的内容发送到所有其他节点。 我发现 MPI_Bcast 可以做到这一点:

int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype,
    int root, MPI_Comm comm)

我发现的所有示例都具有已知的 count 值,但在我的例子中,计数值主要在根上是已知的。其他 examples 说 MPI_Bcast 的相同调用检索其他节点上的数据。

我已经添加了这个:

typedef short Descriptor[128];
MPI_Datatype descriptorType;
MPI_Type_contiguous(sizeof(Descriptor), MPI_SHORT, &descriptorType);
MPI_Type_commit(&descriptorType);



 if(world_rank == 0)   {
  struct stat finfo;

  if(stat(argv[1], &finfo) == 0) {
        querySize = finfo.st_size/sizeof(Descriptor);
  }

 {
  //read binary query
  queryDescriptors = new Descriptor[querySize];
  fstream qFile(argv[1], ios::in | ios::binary);
  qFile.read((char*)queryDescriptors, querySize*sizeof(Descriptor));
  qFile.close();

  }
}

  MPI_Bcast((void*)&querySize, 1, MPI_INT, 0, MPI_COMM_WORLD);
  if (world_rank != 0)
  {
        queryDescriptors = new Descriptor[querySize];
  }
  MPI_Bcast((void*)queryDescriptors, querySize, descriptorType, 0, MPI_COMM_WORLD);

当我这样调用它时:mpirun -np 2 ./mpi_hello_world 它工作正常,但是当我用超过 2 调用它时,我得到这个:

mpi_hello_world: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.
mpi_hello_world: malloc.c:3096: sYSMALLOc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 * (sizeof(size_t))) - 1)) & ~((2 * (sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long)old_end & pagemask) == 0)' failed.

【问题讨论】:

  • 所以发出两个广播,第一个带有计数,第二个带有缓冲区内容。
  • 你是对的,这是一个解决方案。我想知道 MPI 中是否有针对这种情况的机制。
  • 我不知道,但我的 MPI 有点生锈了。
  • 马克是对的——唯一的解决办法是使用两个广播。与常规的点对点通信不同,MPI 无法提前探测广播消息。事实上,这也适用于所有集体电话,例如MPI_SCATTERMPI_GATHER
  • 我使用了 Mark 指出的解决方案,但由于第二个 MPI_Bcast,querySize 为 23,我收到此错误。我正在处理单个节点,这会是一个问题吗?

标签: c++ mpi


【解决方案1】:

如果qFile.read(...) 未包含在if(rank==0){} 测试中,则所有进程都将读取该文件。对于除 0 之外的所有进程,queryDescriptors = new Descriptor[querySize]; 应该在第一个 MPI_Bcast() 之后调用:之前,querySize 在这些进程上毫无意义。

进程 0 必须:

  • 读取项目数
  • 分配
  • 读取数组
  • 广播项目数
  • 广播数组

其他进程必须:

  • 接收物品数量
  • 分配
  • 接收数组

这是一个如何读取浮点数组并使用动态分配的示例:

#include <stdio.h>
#include <iostream>
#include <fstream>

#include <mpi.h>
using namespace std;

int main (int argc,  char *argv[])
{
    int rank;
    int size;

    MPI_Init(&argc, &argv);

    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    if(rank == 0)
    {
        //creating the file
        ofstream myfile;
        myfile.open ("example.txt", ios::out |ios::binary);
        int nbitem=42;
        myfile.write((char*)&nbitem,sizeof(int));

        float a=0;
        for(int i=0;i<nbitem;i++){
            myfile.write((char*)&a,sizeof(float));
            a+=2;
        }
        myfile.close();    
    }


    //now reading the file
    int nbitemread=0;
    float* buffer;
    if(rank==0){
        ifstream file ("example.txt",  ios::in |ios::binary);
        file.read ((char*)&nbitemread, sizeof(int));
        buffer=new float[nbitemread];
        file.read ((char*)buffer,nbitemread* sizeof(float));
        file.close();
        //communication
        MPI_Bcast(&nbitemread, 1, MPI_INT, 0, MPI_COMM_WORLD);
        MPI_Bcast(buffer, nbitemread, MPI_FLOAT, 0, MPI_COMM_WORLD);
    }else{

        MPI_Bcast(&nbitemread, 1, MPI_INT, 0, MPI_COMM_WORLD);
        //nbitemread is meaningfull now
        buffer=new float[nbitemread];
        MPI_Bcast(buffer, nbitemread, MPI_FLOAT, 0, MPI_COMM_WORLD);

    }

    //printing...
    cout<<"on rank "<<rank<<" rode "<<buffer[nbitemread/2]<<" on position "<<nbitemread/2<<endl;

    delete[] buffer;
    MPI_Finalize();

    return 0;
}

使用mpiCC main.cpp -o main 编译并通过mpirun -np 2 main 运行

您的代码中的另一个问题是MPI_Type_contiguous(sizeof(Descriptor), MPI_SHORT, &amp;descriptorType);。应该是MPI_Type_contiguous(sizeof(Descriptor), MPI_CHAR, &amp;descriptorType); 这是一段基于您的代码,应该可以解决问题:

#include <stdio.h>
#include <iostream>
#include <fstream>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <mpi.h>
using namespace std;

int main (int argc,  char *argv[])
{
    int world_rank;
    int size;

    MPI_Init(&argc, &argv);

    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

    int querySize;


    typedef short Descriptor[128];
    MPI_Datatype descriptorType;
    MPI_Type_contiguous(sizeof(Descriptor), MPI_CHAR, &descriptorType);
    MPI_Type_commit(&descriptorType);


    Descriptor* queryDescriptors;


    if(world_rank == 0)   {
        struct stat finfo;

        if(stat(argv[1], &finfo) == 0) {
            cout<<"st_size "<<finfo.st_size<<" descriptor "<<sizeof(Descriptor)<< endl;
            querySize = finfo.st_size/sizeof(Descriptor);
            cout<<"querySize "<<querySize<<endl;
        }else{
            cout<<"stat error"<<endl;
        }

        {
            //read binary query
            queryDescriptors = new Descriptor[querySize];
            fstream qFile(argv[1], ios::in | ios::binary);
            qFile.read((char*)queryDescriptors, querySize*sizeof(Descriptor));
            qFile.close();

        }
    }

    MPI_Bcast((void*)&querySize, 1, MPI_INT, 0, MPI_COMM_WORLD);
    if (world_rank != 0)
    {
        queryDescriptors = new Descriptor[querySize];
    }
    MPI_Bcast((void*)queryDescriptors, querySize, descriptorType, 0, MPI_COMM_WORLD);

    cout<<"on rank "<<world_rank<<" rode "<<queryDescriptors[querySize/2][12]<<" on position "<<querySize/2<<endl;

    delete[] queryDescriptors;

    MPI_Finalize();

    return 0;
}

【讨论】:

  • 很抱歉没有提到它,但我大多也是这样做的。我使用 mpic++ 和 mpirun -np 3 main
  • 我在我的问题中添加了更多代码。法国万岁!
  • 我想知道这是否是一个问题,因为我在单个节点上运行它?
  • 罗马尼亚特拉亚斯卡!这应该不是问题,除非文件真的很大。由于整个文件是广播的,因此副本与进程一样多(可能更多用于消息传递)。如果你的文件比你的 RAM 小 20 倍,它应该不会失败。这个比例是多少?此外,如果您使用集群,您可能需要向作业管理器指定内存要求。对于SLURM,它可能是#SBATCH --mem 1G。此限制的默认值通常较低,指定更高的值可能会有所帮助。
  • cat /proc/meminfo 说:2052392 kB,文件为:5888 字节。我还没有使用集群,只是 mpirun 和主机参数。
猜你喜欢
  • 2013-12-20
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
  • 2014-07-21
相关资源
最近更新 更多