早崎トップ 研究(気候気象) 研究(大気汚染) データリスト Linux Tips Mac Tips

Fortra90 個人メモ

私的Fortran90Tips

Fortran90/95 に関する個人的メモ.

基本方針

  • FORTRAN77 と Fortran90/95 との違いに特に着目
  • 記入ルールを明確に定義し,記述に際してはそれを守る (個人的努力目標.できるだけやってみる)

個人的記入ルール

F90/95 記述ルール

別ページ(F90/95個人的記入ルール)に記載

Makefile 関連ルール

複数コンパイラ(Ex. Intel Fortran & gfortran)が利用できる時には, コンパイラオプションがMake時に切り替わるようにしておきたい. 泥臭い方法だが,ひとまずは Makefile 内の条件分岐で コンパイラオプションを強制的に差し替え.

(Makefile 冒頭に記述)
--- ここから
ifeq ($(F90),ifort)
  #F90 = ifort
  FFLAGS = -assume byterecl -convert big_endian
  FINAL_FFLAGS = -parallel -O3
else
  ### for gfortran
  #F90 = gfortran
  FFLAGS = -fconvert=big-endian -ffree-line-length-none
  FINAL_FFLAGS = -O3
endif
--- ここまで

コピー & ペーストすると行頭部分にスペースが入ってしまうので要注意.

私がファイル編集に使うエディタは vi (vim) である. 私的デフォルト設定ではタブをスペース変換している. しかし,Makefile 編集時に入れるタブはスペースに変換してはダメ (See Linux個人設定 # vim; ~/.vimrc にて,noexpandtab を設定).

F90 文法・書式・使用方法

自分自身が忘れやすい文法などのメモは, F90 文法・書式・使用方法 にまとめた.

Fortran の基本文法は,各自が本読んで勉強してください. 参考文献をページ末尾に挙げておきます.

  • 基本はモジュール化する. モジュール化の際には,あまり複雑にしない. モジュール内のサブルーチンには,単純な機能だけを与える. ある作業に特化したモジュールは,他のプログラムへの転用が難しい
  • コンパイルは Makefile にておこなう. モジュール化すると,手動でコンパイルするのは面倒になる(というか,ムリ)ので
  • 複数のプログラムで利用可能な,汎用性の高いモジュールは, 共通置き場に置く.

以後は気が向いたら追記

Intel Fortran に関するメモ

ここでは, Intel Fortran 11.1 の使用を前提とする.

  事前準備

CentOS の場合,開発環境をあらかじめ入れておく方が良い. Intel Fortran, C, C++ のインストールには直接関係ない(はずだ)が, 後で様々な関連ソフト・ライブラリを追加インストールする際に必要なものである.

CentOS 5.x 系列の場合

# yum groupinstall "Development Libraries" "Development Tools"
# yum install compat-libstdc++-33.x86_64   
(netCDF の configure & make 時に必要)

# yum install libXaw Xaw3d
# yum install libXext

並列処理・OpenMP に関するメモ

コンパイラによる自動処理または OpenMP directive を明示しての 並列処理について.

なお,プログラムの書き方によっては, 並列処理をすることで動作が遅くなることもある. 使いかたは要注意.

少なくとも,single thread で正常動作することを確認してから, (必要ならば)並列処理を考えるべき.

 並列処理の基本・概要

あまり込み入ったことはおこなわない. もし私が数値モデル実行 & その解析をメインの仕事としてるなら, 並列処理に関するノウハウを勉強する意義もあるが, 私の場合はデータ解析がメイン.

データ解析では,必然的にHDD上のデータの読み書きが多く, 並列処理導入で得られる利益が(今のところ)少ないため(記述: 2012-01-26).

 OpenMPによる並列処理

データ解析において multi-thread CPU の性能を活かすとしたら,

相互依存関係のない DO ループの並列処理

というのが最も妥当な利用法だろう. 特に,ループ内で HDD へのデータ入出力を伴わない事が必要だろう.

例えば以下のようなもの:

!$omp parallel do
  DO iy = 1, ny
    DO ix = 1, nx

      (Fortran の命令.各種計算)
      ※データ依存性に注意!
      フロー依存性,反依存性,出力依存性, ...

    END DO    ! ix
  END DO    ! iy
!$omp end parallel do

上記の場合,最も外側のループだけが並列化されるようだ. なお,DO WHILE 分は,ループ開始時点で反復回数が不明なので,並列化できない.

コンパイルオプションとして,ifort ならば -openmp をつける.

ifort -openmp hoge.f90

多重ループになっている場合,どこに置いたら最も効率的な並列化が可能か? 私はそこまで知らないし,たぶん知る必要もないだろう. 想像だが,DO loop の繰り返し数が多いものを分割するのが良さそうだが.

多重ループにするから,考えるのが面倒になる. いっそのこと,DO loop をひとつにしてしまえばいい.

ただし,DO loop 内の演算部分で,データ依存性 (data dependences) が存在すると, 正しい結果が得られない. このへんは, 牛島 (2006, OpenMPによる並列プログラミングと数値計算法) のsec. 2.6 「データ依存性の除去」などを参考に. 自分の計算で,どの部分に依存性があるのかは,自分で考えること.

 自動並列処理 (-parallel -O2 ないし -O3)

OpenMP directive を入れ間違えると遅くなるかもしれない. 知識や自信のない人,または「そんなことを学ぶ時間もない」という人は, 試しに -parallel -O3 あたりをコンパイルオプションに入れて 実行してみるのもよいかもしれない.

ifort -assume byterecl -convert big_endian -parallel -O3 hoge.f90

" -assume byterecl -convert big_endian " のオプションは並列化と無関係. 単に私がデフォルトで使っている FFLAGS である.

並列化すべきか否かという基準は,利用者毎に多様であろう. 万人に適合する基準などない. 個人的な経験で言うなら,

数時間以上の時間を節約出来るなら,並列処理の導入を検討する

となるであろうか.

ものの数秒〜数分で終了するようなデータ解析プログラムなら, 並列処理など考えるだけ時間の無駄. 高速化に時間を費しても,return (節約できる時間)があまりにも少なすぎる.


並列化の結果,処理速度が劇的に改善されるならば, 並列処理を学ぶ意欲も湧くだろう. 遅くなる・ほとんど変わり映えしないのなら,そのプログラムは並列処理が不要か, あるいは並列処理できないようなコーディングであるか,だろう.

一般論でいうと,データ入出力(HDDの読み書き)が多いプログラムは, 並列化による恩恵が少ないと思う. メモリ空間上での数値演算処理が多いものは,並列化できると いい気分になるかもしれない(単なる自己満足かもしれないが).

私が並列処理を利用して効果を実感したのは,以下のような場合:

  • 時系列データの数値フィルタリング. 長期の客観解析データ(6-hourly)から,synoptic eddy 成分(例えば8日以下の周期帯)を 抜き出すとき.
    • ある1地点の時系列データ数だけみれば, たいした数ではない(6-hourly & 30-year で 44000個くらい)が, それを全格子点(JRA25 なら,288 * 145 = 41760個)について実行するなら, 地点数の分だけ繰り返し処理しなければならない
    • 時系列データ処理ならば,隣接する格子点のデータとは相互依存しないので, 格子点のループを分割して並列処理できれば, 処理速度向上が期待できそう(実際,かなり速度向上した)
    • 上記の例で注意すべき事は,全データを配列上に格納するのは危険を伴うこと. 4万の二乗は,1.6E+9 つまり1.6 ギガ個の配列が必要. 必要メモリ量は,single precision だとしても 6.4 GB に達する.
    • 実際,客観解析データの高解像度に伴い,格子点数は増加の一途である. 時間間隔も 6-hourly から 3-hourly,さらには hourly になっていくだろう. 10年前に作ったプログラムが,そろそろ実用的でなくなってきた.要検討.
  • ... その他,折を見て記述

 コンパイル時のエラー

単純な記述ミスならば,コンパイル時のエラーメッセージを一つずつ地道に潰せば何とかなる. うんざりする量のエラーが出たとしても,いざデバッグしてみれば 宣言文でのスペルミスなどであることが多い.

以下は単純ミス以外で自分が体験したエラーおよび対処法.

 relocation truncated to fit: R_X86_64_32S against `.bss'

コンパイルは成功するが,実行ファイル作成時(assemble & link時)に下記のようなメッセージが出る:

hoge.f90:(.text+0x77): relocation truncated to fit: R_X86_64_32S against `.bss'

これは,メモリの使用量が2GBを超過するような巨大な配列を切った場合に出やすい. 倍精度の巨大な配列をもつ変数を複数用意すると,結構簡単に生じてしまう. 気象学分野が使うデータの場合, 0.25 deg. x 0.25 deg. 間隔の全球データ(1440 x 720 grids), 60-level, 5-variable を倍精度配列(8-byte)で用意したら, 1440 x 720 x 60 x 5 x 8 (byte) = 2.32 GB.はい,2 GB超過しました.

鉛直層でなく時間方向に配列用意した場合,もっと低解像度のデータでも, 簡単に2 GBくらい超過してしまう.

2GB未満に抑制するのが困難 or 煩わしいならば,コンパイルオプションで対処する.

Intel Fortran ならば,-mcmodel=large -shared-intel をつけてコンパイル.

ifort -mcmodel=large -shared-intel hoge.f90

なお,使用する計算機のメモリ搭載量は必ず確認しておくこと. 本当にそれだけの配列を切る必然性があるのか? 単に自分のコーディングが下手くそなだけなのではないか? よく考えてから使うべし.

 netcdf.mod を利用するモジュールのコンパイル

netcdf.mod (NetCDF のモジュール) を使って,自分用Fortran 90/95 モジュールをコンパイルする際に体験. コンパイルオプションの使い方を,完全に勘違いしていた(checked: 2013-07-25).

以下のようなモジュール (hoge.F90) をコンパイルし,モジュール hoge.mod を作りたい.

MODULE mod_hoge
  USE netcdf

...
END MODULE mod_hoge

ifort の -module オプションでなく,インクルード -I を使うのが正解.

間違い: ifort -c hoge.F90  -module /usr/local/include
正解: ifort -c hoge.F90  -I/usr/local/include

間違えたときのコンパイラのエラーメッセージ例は下記:

$ make mod_times_info.o
ifort -L/usr/local/netcdf/lib -lnetcdf -c -o mod_dump_datetime.o  mod_dump_datetime.F90
ifort -L/usr/local/netcdf/lib -lnetcdf -c -o mod_handle_err.o  mod_handle_err.F90
ifort -c mod_times_info.F90 -L/usr/local/netcdf/lib -lnetcdf -module /usr/local/netcdf/include
mod_times_info.F90(1): error #7001: Error in creating the compiled module file.   [MOD_TIMES_INFO]
MODULE mod_times_info  
-------^
compilation aborted for mod_times_info.F90 (code 1)
make: [mod_times_info.o] Error 1 (ignored)

日本語環境だと,以下のようなメッセージになる:

   [MOD_TIMES_INFO]1): error #7001: コンパイル済みモジュールファイルを作成するときのエラーです。

 実行時のエラー

文法上のミスがなくても,実行時に Segmentation fault (セグメンテーションフォールト) により 停止することも多い.

大抵はユーザのミスが原因だが,それだけではない.

 double free or corruption (out) でコアダンプして落ちる場合

実行時に,"double free or corruption (out)" として落ちる事例が発生(from 2015-05-27 to 2015-06-08). 原因は,配列の上限/下限数を逸脱した事. 具体的には,data(1:100) という配列を宣言したのに,配列番号がゼロ以下や100を超過するなどの場合にあたる.

このような事態を避けるために,配列数の上限/下限値を逸脱するか否かのチェックをするようにコンパイルしておく. 自分の default コンパイルオプションに下記項目を追加.

(ifort の場合) -check bounds
(gfortran の場合) -fbounds-check

DO loop 内で莫大な数の配列を使う場合には,量が多すぎるため ソースコードの一部に標準出力で吐き出させるというわけにも行かず,原因の特定に手間どった. エラーメッセージを真面目に読んで,そのメッセージをそのままネット上にて検索かけると, 上記情報にたどり着いた. やはり,エラーメッセージはきちんと読んでおくべき(checked 2015-06-09, CentOS 7). 実際にトラブルを体験したプログラムは,大気汚染関連データの品質管理(QC)処理の際.

 文字変数が途切れる場合

gfortran では,Fortran 90 以後の free form スタイルが使えると言いながら, デフォルトでは132文字までしか認識しない. とてもながーい文字列を使った処理を行った際に,なぜか文字列が途中で途切れる問題にぶち当たってかなり悩んだ. 運の悪いことに,最後の1-2文字だけが切れており,問題特定するのが遅れた.

man gfortran にて,"free" で検索すれば,該当の記述がある(checked 2015-05-26, CentOS 7).

 Segmentation fault: 巨大な配列の場合

配列数を非常に大きくとる Fortran プログラムを実行した時,Segmentation fault になった. 文法上のエラーは全く無く,実行時のエラーメッセージも Segmentation fault としか出ない. (自分を含め)コンピュータシステムに詳しくないものにとっては,「どこがダメやねん!」と 叫びたくなるような,とても理不尽なエラー.

私が直面した事例の場合,720 x 720 x 73 の配列を2つ使ったプログラムを実行した時 (NOAA weekly snow cover data, EASE Grid ver.2, cell size = 25 km, pentad mean 作成時).

原因は,スタック領域の不足. Fortran 固有のスタック領域不足の場合,-heap-arrays オプションを使ってみる. OpenMP の場合は,-heap-arrays を使わない方が賢明(らしい.理屈は知らない).

(コンパイル時に指定)
ifort -heap-arrays 10 hoge.F90

Linux システム自体のスタック領域不足もありうる. ulimit -s で unlimited を指定. この他にもいくつかの制約あり(ulimit -a で確認).詳細は man ulimit 参照.

(コマンドプロンプトで実行.まずはサイズを確認)
$ ulimit -s
10240
(CentOS 6.5 の場合は 10 MB.これを制限無しにする)
$ ulimit -s unlimited
(実行したシェルの子プロセスにしか引き継がれないので注意.)
(~/.bashrc などに記載しておけ)

参考文献

書籍

21世紀になってから,ようやくFortran90/95 関連の解説本が ちらほら出版されてきた. ちなみに,私が Fortran90 に移行を決意したのは, 確か2005年くらいだったと思う.

それまでは,まともな解説本が無くて(自分では見つけられなくて,の意) 移行する踏ん切りがつかなかった.

以下に,私がお薦めする Fortran 解説本を示す.

1. 数値計算のための Fortran90/95プログラミング入門
(牛島省 著,森北出版,2007年)
module に関する記載が豊富. 私が Fortran 90 のモジュール化に踏み切るきっかけをくれた本. モジュール化を推進するため,makefile を使うことにもつながった.
それまでは,必要な外部サブルーチンがあれば シェルスクリプト内で cat などで一つの Fortran ソースにまとめてから コンパイルしていた. makefile の利用 & モジュール化することで, パーツ(module)単位でのコンパイルが可能となり, プログラムデバッグに要する時間の節約につながった. 私にとっては買ってよかった本の一つ.

2. 実践 Fortran90 プログラミング 入門から応用まで 第2版
(田辺誠・平山弘 著,共立出版,2001年)
Fortran90関連で,私が初めて購入した書籍. 前述の1を買うまで,これを参照して Fortran90 を書いていた. 1よりも初級者向き.FORTRAN77 をよく知る人が読むと,少し物足りないかも. しかし,私がFortran90を書き始めた頃には, 配列の動的割り当て(allocate 関係),配列関数, 文字列の組込み関数 (trim 関係),構造体などについて何度もお世話になった. select 文や inquire 文を多用するようになったのも, この本がきっかけ.

3. 電子計算機のプログラミング=8 FORTRAN77 入門 改訂版
(浦昭二 編,培風館,1990年改訂)
FORTRAN77 本なので,当然記述は古い. しかし,私はこれで FORTRANを学んだので,購入して20年近く経過しても いまだに所持しつづけている. 組込み関数の一覧表は,現在でも最も閲覧しやすいと思う. 古いけれど良書.

4. Numerical Recipies in Fortran, Second Edition
(Press et al., Cambridge University Press, 1992)
これもFORTRAN77本だが,数値計算に関するサンプルが豊富. 現在は third edition が利用可能 (2011-06-30 確認). 書籍内のコードは,添付の CD-ROM や web 経由ダウンロードなどで利用可能. この本を買わなかったら,私は修論書けなかっただろう. 心から感謝している. on-line electoronic book もある.
詳細は, Numerical Recipies Home Page を参照.

5. OpenMPによる並列プログラミングと数値計算法
(牛島省 著,丸善出版,2006年)
OpenMP に関する解説本. 私は詳しくないので評価しかねるが,おそらくは基本的なことだけを書いているはず. また,2006年時点での記述なので,当然ながらその後のOpenMP のバージョンアップに伴う 新機能についての解説はない. OpenMP の基本の「き」を知るには有用と思われる.特に2章まで (2015-07-28 確認).

オンライン資料(web, PDF)

  • インテルFortranコンパイラー OpenMP 活用ガイド
    • Intel Fortran の日本国内販売代理店である,エクセルソフトが制作. PDFファイルとして閲覧可能(checked 2015-07-28).
    • Intel Fortran ver. 9 向けに書かれているので,かなり古い.ただし,基本的な機能を学ぶだけなら,これで十分だろう.

更新履歴

Date Changes
2016-02-21 ページ内の参照用IDを付け替え. 番号付けのルールは, Linux tips (hysk) # 自分専用ルール・HTML に従う.
2013-07-25 Fortran90 個人メモ (hysk) # netcdf.mod を使用するモジュールのコンパイル を追加.