前回まででMPIの基本的機能は説明しました。ですが通信については組み込み型のみを対象としていました。
MPIは1991年に開発されており、当時のfortranの仕様であった手続き型パラダイムで書かれています。その後オブジェクト指向パラダイムが主流となり、fortranもfortran2003でオブジェクト指向がサポートされました。オブジェクト指向言語ではクラスとそのインスタンスを対象にプログラミングします。今回はそのようなクラスをMPIで扱う方法を解説します。
目次
- 並列処理とは
- [プロセス並列の基本] (https://qiita.com/items/5cce1174ac48367948bc)
- [集団通信] (https://qiita.com/Bluepost59/items/f1871b37669b38352a7c)
- [1対1通信] (https://qiita.com/Bluepost59/items/f1426e8cb098d6632402)
- オブジェクト指向コードの並列化 ←今ココ
クラスを通信する
MPIデータ型
MPIで通信するためには、前回までで述べた通り、送受信データの型を指定する必要があります。それはMPIで定義された型でなければならず、integerやdouble precisionなど組み込み型のみが定義されています。
つまり、自分で定義したクラスはそのままではMPIコマンドを使って通信することができません。そのため自分で「クラスのすべてのメンバを通信してやる」というコマンドも用意してやる必要があります1。
実装する場所
サブルーチンの実装の場所については、次のようにいくつかの選択肢が思いつきます。
- 通常のモジュールサブルーチンとして。
- 通信するクラスをコンポジットするクラスのメンバ関数として。
例えば、particleをメンバに持つsystemクラスのメンバ関数として。 - 通信するクラスのメンバ関数として。
基本的にはどの方法でも実装できます。通信するクラスのメンバ関数にするのは少し難しそうな気もしますが、特に問題なく動作します。
例
例として、次のクラスを通信します。
type smp
integer :: id
double precision :: xx
character(len=40) :: name
contains
procedure :: return_id => smp_return_id
procedure :: bcast => smp_bcast
end type smp
bcastをメンバとして実装する
subroutine smp_bcast(self,ierr)
class(smp) self
integer :: ierr
call mpi_bcast(self%id, 1, mpi_integer, 1, mpi_comm_world, ierr)
call mpi_bcast(self%xx, 1, mpi_double_precision, 1, mpi_comm_world, ierr)
call mpi_bcast(self%name, 40, mpi_character, 1, mpi_comm_world, ierr)
end subroutine smp_bcast
MPIだからといって大変なところは、ierrを作らないといけないことくらいしかありません。実際のところ、MPIコマンドの組み立て上通信されるクラスのメンバにするよりは、それをコンポジットにするクラスのメンバとして実装するのが設計しやすいと思います。
注意点
-
クラスを使う場合、クラスは別ファイルのmoduleとして宣言して分割コンパイルする場合も多いと思いますが、use文でonlyを使わない場合、mpif.hのincludeは一番最初にコンパイルするものだけにしないと多重取り込みでエラーが出ます。
-
fortranのcharacter型はC言語のように中身は配列なので、配列同様の扱いをする必要があります。例えばallocateしないで使ったりはできません。
-
C++のboostのmpiでは構造体やクラスも送れるので、ひょっとするとうまくやるとfortranでもできるのかも知れません。僕はよく知りません。 ↩