MPI_type 自定义类型

上一次尝试了使用Send/Recv传递简单的build-in类型的数据。

 

有时候人们需要一次性传递多种不同类型不同数量的build-in类型数据,通常将数据组织成struct或class来实现。对于这种需求MPI提供了多种支持方式,如MPI_Type_vector、MPI_Type_indexed和MPI_Type_struct等。MPI_Type_struct可能是最通用的自定义类型生成器,因此下文主要是描述如何使用这种方式进行数据传递。

 

首先,展示一下需要传递的自定义数据

  1. class CTestSendRecv  
  2. {  
  3. public:  
  4.     CTestSendRecv(){}  
  5.     int m_n;  
  6.     double m_d;  
  7.     double m_arr[4];  
  8.     float m_f;  
  9. };  

 

如果要传递这样一个自定义类型的数据,MPI要求注册一个新的数据类型,注册时必须详细描述CTestSendRecv的数据结构及其成员对应的MPI类型定义(MPI_INT、MPI_DOUBLE等)。相关的函数调用和数据定义代码如下

  1. int blockLength[] = {1,1,4,1};  
  2. MPI::Datatype oldTypes[] = {MPI_INT,MPI_DOUBLE,MPI_DOUBLE,MPI_FLOAT};  
  3.   
  4. //直接计算偏移量,每一个数据以double对齐  
  5. MPI::Aint addressOffsets[]={0,sizeof(double),2*sizeof(double),6*sizeof(double)};  
  6.   
  7. MPI::Datatype newType =  
  8.         MPI::Datatype::Create_struct(  
  9.         sizeof(blockLength)/sizeof(int), /**/  
  10.         blockLength,  
  11.         addressOffsets,  
  12.         oldTypes);  
  13. newType.Commit();  

blockLength数组是用来描述CTestSendRecv中各个成员对应的build-in数据个数。

oldTypes数组是用来描述CTestSendRecv中各个成员对应的build-in数据的MPI类型定义。

addressOffsets是用来描述CTestSendRecv中各个成员离该CTestSendRecv类的首地址的偏移量,这里涉及到编译器的字节对齐的知识。当然MPI提供了MPI_Get_address这样的函数来计算偏移量,但使用起来较为麻烦。

MPI::Datatype::Create_struct为CTestSendRecv创建新的MPI类型。

newType.Commit();提交注册新的MPI数据类型。对应的注销数据类型的函数是newType.Free();

 

下面是完整的代码示例

  1. int main(int argc, char* argv[])  
  2. {  
  3.     Cmpi mpi;  
  4.     mpi.PrintMachineInfoWithStdIO();  
  5.   
  6.     int blockLength[] = {1,1,4,1};  
  7.     MPI::Datatype oldTypes[] = {MPI_INT,MPI_DOUBLE,MPI_DOUBLE,MPI_FLOAT};  
  8.   
  9.     //直接计算偏移量,每一个数据以double对齐  
  10.     MPI::Aint addressOffsets[]={0,sizeof(double),2*sizeof(double),6*sizeof(double)};  
  11.   
  12.   
  13.     MPI::Datatype newType =  
  14.             MPI::Datatype::Create_struct(  
  15.             sizeof(blockLength)/sizeof(int), /**/  
  16.             blockLength,  
  17.             addressOffsets,  
  18.             oldTypes);  
  19.     newType.Commit();  
  20.   
  21.     const int nMsgTag =999;     /*消息标识号 */  
  22.     int nSendProcessId(1);  /*发送数据的进程的编号*/  
  23.     int nRecvProcessId(0);  /*目的进程的标识号*/  
  24.     const int minProcessorCount =2;  
  25.   
  26.     if (mpi.GetTotalProcessCount() < minProcessorCount) {  
  27.         std::cout<<"Not enough processor"<<std::endl;  
  28.         return 0;  
  29.     }  
  30.   
  31.     if (mpi.GetCurrentProcessId()==nSendProcessId) {  
  32.         CTestSendRecv testSend;  
  33.         testSend.m_d =5.6;  
  34.         testSend.m_f =8.2f;  
  35.         testSend.m_n =11;  
  36.         testSend.m_arr[0]= 1.1; testSend.m_arr[1]= 2.2; testSend.m_arr[2]= 3.3;testSend.m_arr[3]= 4.4;  
  37.   
  38.         MPI::COMM_WORLD.Send(&testSend, /*发送数据的内存地址*/  
  39.                 sizeof(testSend)/sizeof(CTestSendRecv),             /*发送数据的数目*/  
  40.                 newType,        /*发送数据的类型*/  
  41.                 nRecvProcessId,     /*目的进程的标识号*/  
  42.                 nMsgTag             /*本消息标识号 MPI_ANY_TAG*/  
  43.                 );  
  44.     }  
  45.     else {  
  46.         CTestSendRecv testRecv;; /*接收数据的变量*/  
  47.   
  48.         if (nSendProcessId >= mpi.GetTotalProcessCount() ) return 0;  
  49.         MPI::COMM_WORLD.Recv(&testRecv,  
  50.                 sizeof(testRecv)/sizeof(CTestSendRecv),  
  51.                 newType,  
  52.                 nSendProcessId,  
  53.                 nMsgTag);  
  54.         std::cout<<"Data="<<testRecv.m_arr[2]<<std::endl;  
  55.     }  
  56.   
  57.     newType.Free();  
  58.   
  59.     return 0;  
  60. }  

 

这样就可以达到了传递自定义数据类型的目的。

从以上的代码看来,MPI必须以静态的方式了解自定义数据结构的组成,所以可能不支持模板类型的自定义数据。

还有一个遗留问题就是,MPI能否支持如下方式的嵌套自定义数据CNestTestData,还有待进一步验证。

  1. class CTestSendRecv  
  2. {  
  3. public:  
  4.     CTestSendRecv(){}  
  5.     int m_n;  
  6.     double m_d;  
  7.     double m_arr[4];  
  8.     float m_f;  
  9. };  
  10.   
  11. class CNestedTestData  
  12. {  
  13. public:  
  14.     char m_c[5];  
  15.     CTestSendRecv m_testSendRecv;  
  16. };  

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
从您提供的代码来看,您已经使用了 `MPI_Type_vector()` 函数来创建一个自定义的数据类型 `col_type` 和 `row_type`,用于将矩阵 `B` 的列和行发送到不同的进程中。但是,在使用 `MPI_Scatter()` 函数进行散播时,您似乎没有使用正确的参数传递自定义的数据类型。 在使用 `MPI_Scatter()` 函数时,第二个参数应该指定要发送的数据块的数量,而不是每个进程应该接收到的数据块的数量。因此,您应该将第二个参数设置为 `num_cols`,表示要发送的列数,而不是 `1`,表示每个进程应该接收到的列数。此外,您应该使用 `col_type` 而不是 `row_type` 来发送数据到列通信子中的进程。 以下是修改后的代码片段: ``` MPI_Comm_split(MPI_COMM_WORLD, col_color, col_key, &col_comm); MPI_Comm_split(MPI_COMM_WORLD, row_color, row_key, &row_comm); MPI_Type_vector(n, block_size, n, MPI_DOUBLE, &col_type); MPI_Type_commit(&col_type); MPI_Type_vector(block_size, n, n, MPI_DOUBLE, &row_type); MPI_Type_commit(&row_type); MPI_Scatter(&B[0][0], num_cols, col_type, &B_col[0][0], block_size * n, MPI_DOUBLE, 0, col_comm); ``` 请注意,这里的 `num_cols` 应该是矩阵 `B` 中列的数量,而不是在创建列通信子时指定的进程数。因此,您需要在代码中设置一个新的变量来存储矩阵 `B` 中的列数,并将其传递给 `MPI_Scatter()` 函数。 另外,如果您发现每个进程接收到的数据仍然不正确,那么您可能需要检查自定义数据类型的定义是否正确,并确保每个进程在接收数据时使用正确的数据类型

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值