将 mpi 消息从 c++ 代码发送到 fortran 90 代码

Posted

技术标签:

【中文标题】将 mpi 消息从 c++ 代码发送到 fortran 90 代码【英文标题】:send mpi message from a c++ code to fortran 90 code 【发布时间】:2012-08-10 06:37:03 【问题描述】:

我正在尝试查看是否可以将 c++ 代码中的数组内容发送到 fortran 90 代码。我正在使用使用 intel 11.1.072 编译器构建的 openmpi 1.4.3。它们安装在 Linux 版本 2.6.18-108chaos (mockbuild@chaos4builder1) (gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-48)) 上。

这里是c++端:

# include <cstdlib>
# include <iostream>
# include <mpi.h>
using namespace std;

void printarray (float arg[], int length) 
   for (int n=0; n<length; n++)
    cout << arg[n] << " ";
  cout << "\n";


int main(int argc, char *argv[] )
   float a[10];
   int myrank,i;
   MPI::Init ( argc, argv );
   myrank=MPI::COMM_WORLD.Get_rank();
   cout << "rank "<<myrank<<" is c++ rank."<<std::endl;
   for (i=0;i<10;i++)
      a[i]=10.0;
   
   printarray(a,10);
   MPI::COMM_WORLD.Send(&a[0],1,MPI::DOUBLE_PRECISION,1,100);
   MPI::Finalize();

这是 f90 的一面:

program main
implicit none
include "mpif.h"
integer:: ierr,stat(MPI_STATUS_SIZE)
real(8):: a(10)

call mpi_init(ierr)
a=0
print*,a
call mpi_recv(a(1),10,MPI_DOUBLE_PRECISION,0,100,MPI_COMM_WORLD,stat,ierr)
print*,a
call mpi_finalize(ierr)
end program

编译完这两个代码后,我运行

$mpirun -n 1 *c_executable* : -n 1 *fortran_executable* > output

我在 fortran 方面得到的数字不是 10.0。

【问题讨论】:

不要使用 MPI C++ 绑定!它们在 MPI 3.0 规范的第 2 版草案中被删除,这种更改很可能使其成为最终的 3.0 标准。请改用 C 绑定 - 它们与 Fortran 的绑定非常相似。还要使用 Fortran 中的 MPI 模块接口:USE mpi 而不是 include "mpif.h" 【参考方案1】:

MPI 标准确实对语言互操作性做出了规定——MPI 2.2 文档的整个 §16.3 专门针对 Fortran 和 C 之间的语言互操作性。

§16.3.10 跨语言交流

MPI 中通信的类型匹配规则并没有改变:发送的每个项目的数据类型规范应在类型签名中匹配用于接收该项目的数据类型规范(除非其中一种类型是 MPI_PACKED)。此外,消息项的类型应与相应通信缓冲区位置的类型声明相匹配,除非类型为MPI_BYTEMPI_PACKED。如果符合这些规则,则允许跨语言交流。

然后它继续展示一个示例,其中使用相同的构造数据类型从 Fortran 代码发送消息并在一段 C 代码中接收它。该类型的构造是为了允许 C 代码将数据接收到属于 Fortran 代码的缓冲区中,但与您的问题更相关的是 C 代码使用从 Fortran MPI_REAL 构造的数据类型.如果有意义的话,在 C 函数中使用 Fortran 数据类型(反之亦然)是完全合法的:

§16.3.6 MPI 不透明对象 - 数据类型

...如果以一种语言定义的数据类型用于以另一种语言进行的通信调用,则发送的消息将与从第一种语言发送的消息相同:访问相同的通信缓冲区,并且如果需要,执行相同的表示转换。所有预定义的数据类型都可以在任何语言的数据类型构造函数中使用。如果提交了数据类型,则它可以用于任何语言的通信。

(已提交预定义的 MPI 数据类型,如 MPI_REALMPI_DOUBLE

相反,一方面使用 Fortran 数据类型,另一方面使用 C 数据类型是允许的,但被认为不可移植:

§16.3.10 跨语言交流

... MPI 实现可能会削弱这些类型匹配规则,并允许使用 Fortran 类型发送消息并使用 C 类型接收消息,反之亦然,当这些类型匹配时。即,如果 Fortran 类型 INTEGER 与 C 类型 int 相同,则 MPI 实现可能允许使用数据类型 MPI_INTEGER 发送数据并使用数据类型 MPI_INT 接收数据。 然而,这样的代码是不可移植的。

(强调我的)

REAL(8) 更改为DOUBLE PRECISION 并不能增加代码的可移植性,因为Fortran 标准不保证DOUBLE PRECISION 类型的表示 - 它只是说DOUBLE PRECISION 是一种替代说明符REAL 类型,即双精度类型,其小数精度应大于默认实数。发送带有MPI_DOUBLE_PRECISION 数据类型的REAL(8) 是不可移植的。相反,可移植程序将使用 Fortran 的 SELECTED_REAL_KIND 内在函数和 MPI_Type_create_f90_real 来注册匹配的 MPI 数据类型。

恕我直言,最好的选择是依靠 C 和 Fortran 之间的语言互操作性,并在双方都坚持相同的数据类型。由于您的编译器套件足够新,您可以使用 Fortran 的 ISO_C_BINDING 机制来获得与 C 兼容的 REALINTEGER 类型,并在 Fortran 调用中使用 C 数据类型。例如:

USE, INTRINSIC :: ISO_C_BINDING
REAL(C_DOUBLE), DIMENSION(10) :: darray
INTEGER(C_INT) :: ival
...
CALL MPI_SEND(darray, 10, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, ierr)
CALL MPI_BCAST(ival, 1, MPI_INT, 0, MPI_COMM_WORLD, ierr)
...

【讨论】:

+1 表示彻底! “将 REAL(8) 更改为 DOUBLE PRECISION 对增加可移植性没有任何帮助”——嗯,确实如此,因为对于所有编译器,REAL(8) 不一定存在(或意味着人们可能期望的),而双精度却存在。 OTOH,BIND(C) 是一个好主意,但在实践中,对于您今天可以想象的任何计算机,浮点表示对于 Fortran 或 C 来说都是相同的——更大的担忧可能是交叉-两个程序的架构(从小端到大端),您必须完全跳过不同的套圈。 @JonathanDursi,大多数通用 MPI 实现(包括 Open MPI)支持异构环境(在 Open MPI 中,它是一个配置时选项,出于性能原因默认禁用)并将进行自动表示转换(endinanness等)在具有不同架构的节点之间发送的数据。应该可以使用可移植的 C 类型及其 ISO_C_BINDING 对应物优雅地处理这种情况,并保持在 MPI 数据类型系统允许的范围内(例如,MPI_BYTE 没有花哨的东西)。【参考方案2】:

是的,您可以这样做;主要问题是在您的 C++ 代码中,您的 a 数组的类型为 float,而不是双精度数。

您也只发送了 1 个,而不是 10 个,这些非双打; MPI_RECV() 仍然可以工作,但当然不会设置其他 9 个值。

您应该注意的其他一点是,您应该在 C/C++ 中使用 MPI_DOUBLE,在 Fortran 中使用 MPI_DOUBLE_PRECISION;它们不必相同,实际上我想在 C 中使用 MPI_DOUBLE_PRECISION 是未定义的。

您可能还想在 fortran 程序中使用 double precision 而不是 real(8),这很常见但不标准。

原则上,您甚至需要担心异构性,担心运行这两个程序的机器上的浮点数编码,但对我们大多数人来说,这不是问题。

【讨论】:

我在fortran代码中将数组改为双精度,在c++代码中改为double,并将c++ mpi数据类型设置为mpi::double,数组与fortran成功通信边。我认为你是对的,mpi_double_precision 可能在 c 中未定义。谢谢! 混合来自不同语言的 MPI 数据类型超出了标准中的定义 - 请参阅我的答案。【参考方案3】:

这是一个修改后的 C 工作版本

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

main(int argc, char **argv) 

  int i,ierr, num_procs, my_id;
  double a[10];

  for (i=0;i<10;i++)
  
    a[i]=10.0;
  

  ierr = MPI_Init(&argc, &argv);

  printf(" Hello C Code\n"); 

  /**/
  ierr = MPI_Comm_rank(MPI_COMM_WORLD, &my_id);
  ierr = MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
  ierr = MPI_Send(&a[0],10,MPI_DOUBLE,1,100, MPI_COMM_WORLD);  


  ierr = MPI_Finalize(); 


这是 F 中修改后的工作版本

    program main
        use mpi

        implicit none
        integer:: ierr,stat(MPI_STATUS_SIZE)
        double precision:: a(10)

        call mpi_init(ierr)

        write(*,*)"Hello F Code"
        a=0
        print*,a
        call mpi_recv(a(1),10,MPI_DOUBLE_PRECISION,0,100,MPI_COMM_WORLD,stat,ierr)
        print*,a
        call mpi_finalize(ierr)
    end program

【讨论】:

以上是关于将 mpi 消息从 c++ 代码发送到 fortran 90 代码的主要内容,如果未能解决你的问题,请参考以下文章

用于未知消息大小的 MPI 非阻塞发送和接收以及 mpi_iprobe()

如何在 C++ 中使用 MPI 同步和排序打印(任务)

如何在 C++ 中使用 MPI 收集字符字符串并为每个进程打印它们?

分段错误:C++ 中的结构序列化和 MPI 数据传输

C++ 中的 MPI_Send MPI_Recv 段错误

MPI Maelstrom POJ - 1502 ?? Dijkstra裸题