LoginSignup
2

More than 1 year has passed since last update.

posted at

Co-array Fortranって何? どうやって使うの? 調べてみた

東京大学航空宇宙工学科/専攻 Advent Calendar 2020

この記事は東京大学航空宇宙工学科/専攻 Advent Calendar 2020の12日目の記事として書かれています。航空宇宙工学科/専攻の偉大な先輩方の中で雑な記事を書いてしまい申し訳ない限りです。面白い記事が先輩や同期によって書かれているので是非見ていってください。

注意

この文章はMPIなど最低限の並列計算の知識があることが前提になっています。
「MPIなにそれ美味しいの? 」という方はまずはMPIの記事を調べることをお勧めします。
また、私はFortranが好きなだけの素人ですので間違いがあっても許してください…。

はじめに

みなさん、初めまして。航空宇宙工学科B3のFortran大好き人間です。皆さんはFortranと聞くと、「空力の先生が使ってた」か「古代語、使ってる人なんているの」と考えるでしょう。
しかし、Fortranは今でも現役で使われています。 それはスパコンの中で使われています。 Fortranは世界初の高級言語で過去の資産が多く、数値計算が得意なことから今でもスパコンに用いられています。 そのスパコンを動かす際にはMPIなどで各CPUに分散させる必要があります。ただMPIは非常に書くのが面倒です。それを変えようと言うのがPGAS言語です。PGAS言語は分散メモリ型でも共有メモリのように扱えるものです。その中にCo-array Fortranがあります。

今回は、そのCo-arrayについて調べてみました。

Co-array Fortranとは

Co-array FortranとはFortran2008以降に導入された分散並列機能です。MPIと同様に明示的にデータ分散、計算分散、通信、同期を行うことができます。
MPIとの違いとしては文法として並列計算を行えること、MPIでは双方向通信が基本であったが、Co-arrayでは片方向通信が基本、Co-arrayでは[]というイメージ番号のみを用いた通信のプログラミングなので意識せず並列計算プログラミングが可能になることが挙げられます。

コンパイラーごとのCo-array Fortranの環境整備

Intel compiler

Intel® Parallel Studio XE Cluster EditionであればMPI Libraryがついてくるためそのままの環境で使用可能です。
Intel compilerは学生やOSS開発者だと永続ライセンスが無料でもらえますが、for MacだけIntel® Parallel Studio XE Composer Editionとなっており、MPI Libraryが付いてきません。そのためCo-arrayは使用不能なので注意してください。(実際、メイン機がMacの筆者もCo-arrayを使用する際にはLinuxを用いています)

GFortran(GNU,GCC)

GFortranでCo-arrayを使用するためには、外部ライブラリが必要となります。
一般的な外部ライブラリとしてはOpenCoarraysが挙げられます。
インストールはサイトを確認して行ってください。(読者の使うコンピュータに差がありすぎて、一概述べられませんでした)

Hello World!

プログラム

hello.f90
    program hello
      implicit none
      integer::me, p

      p = num_images()
      me = this_image()

      print*,'hello from', me, 'in', p

    end program hello

これが基本的なhello worldの書き方です。num_imagesによって全てのイメージ数を取得し、this_imageにより自分のイメージの番号を取得できます。そのほかは基本的なFortranの記法で書くことができます。

コンパイル

Intel compiler

共有メモリ型の場合

Intel compilerの場合は基本的には-coarray-coarray-num-images=の二つのオプションでコンパイルすることが可能です。(-coarray-num-images=については環境変数で定めている場合は不要)よって以下のようにコンパイルできます

ifort -coarray=shared -coarray-num-images=64  hello.f90

分散メモリ型の場合

-coarrayはデフォルトでは=sharedという共有メモリでの実行が規定されているので、分散メモリで使う際には-coarray=distributedのオプションを使う必要があります。また、分散メモリの場合はバックエンドでMPIを使う必要があるため、MPI実行用のconfigファイルと-coarray-config-fileが必要になります。

hello.conf
-envall -n 64 ./a.out

などとしてください。 -envallはMPIに環境変数を受け渡すオプションです。
分散メモリの場合は

ifort -coarray=distributed -coarray=config-file=./hello.conf ./hello.f90

とすればコンパイル可能です。-coarray-num-images=はconfigfileの中で定義しているので不要となります。

分散メモリ型における別のコンパイル方法

the Intel® Cluster Toolkit licenseをインストールしている場合は

ifort -coarray=distributed -coarray-num-images=64  hello.f90

でも可能です。

GFortran(GNU,GCC)

基本的には

caf hello.f90 

で十分です。

実行

Intel compiler

裏でMPIが走るためいきなり実行ファイルを指定して構いません

./a.out

GFortran(GNU,GCC)

裏でMPIは勝手に走ってくれないため

cafrun ./a.out

mpirun ./a.out 

とする必要があります。

実行結果

hello from 1 in 64
hello from 4 in 64

などといっぱい出てきます。

型宣言・通信・制御

CO配列

CO配列はイメージごとに要素を割り当てる配列です。

integer :: x[5]

のように宣言することができます。上のように宣言しておいて

x[1]=1

とするとイメージ1のxに1が入ります。

x=1

とした場合には全てのイメージのxに1が入ります。もちろんですが

x[1]=1
x[2]=2

とした場合はイメージ1のxには1,イメージ2の2が入ります。

Co-array Fortranの通信

Co-array Fortranの通信は「片方向通信」です。そのため、相手の状態に関係なく通信を行います。よって、同期待ち、コピー回数の削減、オーバラップが可能になります。しかし、データがちゃんと更新されているかはプログラマが保証する必要があるので注意してください。

PUT

co配列の要素は自分自身であれば[]なしで参照でき、他のイメージであれば[]で参照することができます。自分自身の要素を他に送る場合には

if(this_image()==1)x[2]=x

とすることででき、意味はイメージ2にイメージ1のxを渡すことができます(制御の部分で上の書き方の注意事項があります)

GET

上とは反対に自分自身が他のイメージから要素を得る場合には

if(this_image()==2)x=x[1]

とすることででき、意味はイメージ2がイメージ1からxを得るということです。(制御の部分で上の書き方の注意事項があります)

制御

this_image

THIS_IMAGE()

この関数を読んだイメージ番号がintegerで返ってきます

num_images

NUM_IMAGES()

イメージの総数がintegerで返ってきます

critical

critical
 ~~~~
end critical

criticalの間にあるブロックは一度に一つのイメージしかそのブロックを実行できなくなります。
また、ブロックの演算が終わるまで、その他のイメージはイメージ制御されないようにしなければなりません。よって、criticalブロック中ではイメージ制御文は動きません

sync all

sync all

すべてのイメージが同期をとります。
つまり全てのイメージがsync allを実行するまでそれ以降の文の実行は停止されます

PUTおよびGETの注意

上の例でははx[2]にちゃんとしたx[1]が入ってから通信がおこなわれるか、計算がおこなわれるかが不安定です。そのためsync allを入れる必要があります。

sync all
if(this_image()==1)x[2]=x
sync all

sync all
if(this_image()==2)x=x[1]
sync all

sync_image

sync_image(integer / *)

sync_imageは()で呼ばれたイメージは他の関数が自分に対してsync_imageをしないとそれ以降の文は実行されません。よって、下のように使います。

if(this_image()==1)then
sync_image(*)
  !すべてが揃ってからイメージ1にやって欲しいこと
else 
   sync_image(1)
end if

sync memory

~~~ 1番目ににやって欲しいこと
sync memory
~~~ 2番目ににやって欲しいこと

コンパイラで最適化を行うと実行の順番が変わる場合があります。一般には問題ない場合に行われますが、並列計算の場合には問題のある時に行われる場合があります。よって、それを防ぐのがsync memoryです。これを使うことによりsync memoryより前の文がsync memoryより後の分より確実に先に実行されます。

一般に使うMPIの集団通信の関数のプログラムの例

Broadcast

Broadcastは

sync all
  if(this_image() .ne. 1)x=x[1]
sync all

のように書くことができます。MPIより比較的簡単に書くことができることがわかります。

Scatter

scatterは

sync all
  recv(1:n)=send(1:n,this_image())[1]
sync all

のように書くことができます。

Gather

Gatherは

sync all
  recv(1:n,this_image())[1]=send(1:n)
sync all

のように書くことができます。

Reduce

sumをとる関数であれば何も考えなければ

sync all
  if(this_image() .eq. 1)then
   do i=2,num_images()
    x=x+x[i]
   end do
  end if
sync all

のように記述することもできますし、二分木を用いて記述することもここまでお読みの方はできると思います。

また、実際に確認していないのでわかりませんが、

call co_sum(x,xtemp[1])

と書くことができるようです。他にもreduce処理はco_~用意されているようです

プログラムの例

申し訳ありません。時間がなかったのでプログラムを書くことができませんでした。
そのため、いい感じの例のリンクだけ添付しておきます
https://fortran66.hatenablog.com/entry/20120422/1335080600

Co-array vs MPI

https://www.researchgate.net/publication/221597031_CAF_versus_MPI_-_Applicability_of_Coarray_Fortran_to_a_Flow_Solver
などによるとコア数が少ない時にはMPIの方が良いが、コア数が増えるとその性能差は減るとのこと。

終わりに

時間の制約から最後の方は駆け足になってしまい申し訳ありませんが、Co-array Fortranをちょっとでも面白そうだなと思っていただければ幸いです。

そして、Fortranも古代語ではなく日々進化していて並列計算をしやすいように頑張っていると知ってもらえればこれほど嬉しいことはありません。

また、Co-arrayに興味を持った方は日本語の資料や解説書は少ないのでPGAS(Co-arrayなどの分散型だがそれを気にせずにかける言語)言語そのものについてや論文を当たることをお勧めします。

MAKE FORTRAN GREAT AGAIN!!

参考URL

東京大学航空宇宙工学科/専攻 Advent Calendar 2020

http://jjoo.sakura.ne.jp/tips/fortran2008/coarray.html

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2