具有灵活大小的结构的MPI派生数据类型 [英] MPI Derived data type for a struct with flexible size

查看:100
本文介绍了具有灵活大小的结构的MPI派生数据类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C ++发送/接收如下所示的数据结构:

I am trying to send/recv in C++ a data structure that looks like this:

/* PSEUDOCODE */
const int N = getN(); // not available at compile time
const int M = getM();
struct package{
    int    foo;
    double bar;
    /* I know array members do not work this way,
       this is pseudocode. */
    int    flop[N];
    double blep[M];
};

由于MN在运行时是恒定的,因此我可以执行MPI_Type_create_struct(),并且新的数据类型在整个过程中都很好.

Since M and N are constant during runtime, I can do MPI_Type_create_struct() and the new datatype woule be good throughout.

我的问题是如何实现如上所述的数据结构.

My question is how to implement the data structure as described above.

std::vector<>无法使用,因为它不是串行的.

std::vector<> won't work because it's not serial.

灵活的数组成员(例如[][0])在c ++中是未定义的行为,并且不适用于MN中的两个.

Flexible array members like [] or [0] are undefined behavior in c++, and it does not work for the two of M and N.

所以我不得不使用malloc():

class Package {
public:
  // in buffer[]: bar, blep[], foo, flop[]
  // in that order and one directly follows another.
  Package():
    buffer((double*) malloc((M + 1) * sizeof(double) + 
                            (N + 1) * sizeof(int))),
    bar(buffer), blep(buffer + 1),
    foo((int*) (blep + M)),
    flop(foo + 1) {}
  ~Package(){
    free(buffer);
  }

  // construct / free the derived datatype
  static void initialize(unsigned inN, unsigned inM) {
    N = inN;
    M = inM;
    MPI_Aint offsets[2] = {0, (int)(sizeof(double)) * (M + 1)};
    int      blocks[2]  = {M + 1, N + 1};
    MPI_Datatype types[2] = {MPI_DOUBLE, MPI_INT};
    MPI_Type_create_struct(2, blocks, offsets, types, &packageType);
    MPI_Type_commit(&packageType);
  }
  static void finalize() {
    MPI_Type_free(&packageType);
  }

  int send(int rank, int tag) {
    return MPI_Send(buffer, 1, packageType, 
                    rank, tag, MPI_COMM_WORLD);
  }
  int recv(int rank, int tag) {
    return MPI_Recv(buffer, 1, packageType, 
                    rank, tag, MPI_COMM_WORLD, 
                    MPI_STATUS_IGNORE);
  }
private:
  double * buffer;

  static int M;
  static int N;
  static MPI_Datatype packageType;
public:
  // interface variables
  double * const bar;
  double * const blep;
  int    * const foo;
  int    * const flop;
};

int Package::N = 0;
int Package::M = 0;
MPI_Datatype Package::packageType = MPI_CHAR;

我测试了上面的代码,它似乎可以正常工作,但是我不确定我是否正在执行实际上未定义的行为.具体来说:

I tested the above code and it seems to work properly, but I am not sure if I am doing something that is actually undefined behavior. Specifically:

  1. 可以将sizeof()用于MPI_Type_create_struct()吗?我发现一些示例使用MPI_Type_get_extent(),但我不知道有什么区别.

  1. Is it ok to use sizeof() for MPI_Type_create_struct()? Some examples I find use MPI_Type_get_extent(), and I have no idea what is the difference.

我不确定将新数据类型存储在static成员中是否是一个好主意.我发现的示例将其作为参数传递了出去.有什么具体原因吗?

I am not sure if it is a good idea to store the new datatype in a static member. The examples I found instead have it passed around as an argument. Is there any specific reason to do that?

如果此方法可移植,我也会感到困惑.我希望它应该与基于struct的方法一样可移植,但是也许我遗漏了一些东西?

I am also confused if this method is portable. I hope that it should be as portable as struct based methods, but perhaps I am missing something?

推荐答案

如果此方法可移植,我也会感到困惑.我希望它应该与基于结构的方法一样可移植,但是也许我遗漏了一些东西?

I am also confused if this method is portable. I hope that it should be as portable as struct based methods, but perhaps I am missing something?

1.假设不是doubleint,而是某些类型的AB.然后可能会发生类型为B的对象未对齐,该对象将在紧随其后的 A s分配空间.在某些尝试访问此类对象的体系结构中(例如,在(4N + 2)字节边界处的int)将导致 Bus错误.因此,通常情况下,您必须确保在第一个B对象之前进行正确的填充.当您使用struct时,编译器会为您完成.

1. Suppose that instead of double and int you have some types A and B. Then it can happen that an object of type B, for which you allocate space right after As, gets misaligned. On some architectures trying to access such an object (e.g., int at (4N + 2)-bytes boundary) will cause a Bus error. So in the general case you have to ensure correct padding before the first B object. When you use struct a compiler does it for you.

2.访问buffer的方式是UB.本质上,您正在执行此操作:

2. The way you access buffer is UB. Essentially you're doing this:

double* buffer = reinterpret_cast<double*>(malloc(...));
double* bar = buffer;
int* foo = reinterpret_cast<int*>(buffer + 1);

do_something(buffer);
double bar_value = *bar; // This is UB
int foo_value = *foo;    // This is UB, too

这里的问题是在*bar*foo处没有doubleint类型的对象.您可以使用展示位置new:

The problem here is that there are no objects of type double and int at *bar and *foo. You can create them using placement new:

char* buffer = reinterpret_cast<char*>(malloc(...));
double* bar = new(buffer) double;
int* foo = new(buffer + sizeof(double)) int;

请参考此问题.

对于数组,您可以使用std::uninitialized_default_construct来构造给定范围内的对象.

For arrays you can use std::uninitialized_default_construct that constructs objects in the given range.

我不确定将新数据类型存储在静态成员中是否是一个好主意.我发现的示例将其作为参数传递了出去.有什么具体原因吗?

I am not sure if it is a good idea to store the new datatype in a static member. The examples I found instead have it passed around as an argument. Is there any specific reason to do that?

如果NM是静态的,那么将packageType也设为静态似乎很好.如果只有一种类型的Package具有固定的NM,则可能希望避免每次构造Package来创建本质上相同的MPI数据类型时都调用MPI_Type_create_struct.

If N and M are static, then it seems fine to make packageType also static. If you have only one type of Package with fixed N and M, you probably would want to avoid calling MPI_Type_create_struct each time you construct a Package to create essentially the same MPI data type.

但是这种设计看起来不太好:应该在第一次构造之前调用initialize().可能您可以创建一个工厂,该工厂首先创建MPI数据类型,然后根据用户要求使用Package make_package()之类的内容构造Package.然后每个工厂可以有自己的非静态NM.

But this design doesn't look good: one should call initialize() before first construction. Probably you can make a factory that would first create MPI data type and then would construct Package upon user request with something like Package make_package(). Then each factory could have its own non-static N and M.

这篇关于具有灵活大小的结构的MPI派生数据类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆