「配列演算の魔法」を解き明かす:WHEREとFORALLの現場的流儀
こんにちは。かつて巨大なロケットの軌道計算や、流体解析のコードと格闘し続けてきた元アーキテクトです。
CやPythonからFortranの世界に飛び込んできた皆さん、ようこそ。Fortranは「古い」と言われがちですが、実は「メモリの物理配置とハードウェアの性能を直結させる」という点において、未だに右に出るものがない最強の物理演算ツールです。
今日は、Fortranにおける配列操作の強力な武器である`WHERE`と`FORALL`について、教科書には載っていない「現場の裏側」を語りましょう。
—
1. WHERE構文:条件分岐という名の「マスク」
PythonのNumPyを使っている方なら、`arr[arr > 0] = 1`のような書き方はお馴染みですよね。Fortranでこれを行うのが`WHERE`構文です。
! 負の値をゼロにクリッピングする処理
WHERE (A < 0.0)
A = 0.0
ELSEWHERE
A = A 0.5
END WHERE
なぜこれが「速い」のか?
C言語でこれを書くと`if`文による分岐がループ内に発生します。しかし、`WHERE`はコンパイラに対して「条件に一致する要素だけを対象にしたSIMD(ベクトル)演算を生成せよ」という強力なヒントを与えます。
現場での注意点は一つ。`WHERE`はあくまで「マスク処理」です。条件に合致しない要素を無視して演算を進めるため、複雑な条件分岐を入れ子にすると、CPUのパイプラインが乱れ、かえって効率が落ちることがあります。「単純な条件で、広大な配列を叩く」時こそが、`WHERE`の本領発揮です。
—
2. FORALL構文:コンパイラへの「並列化のサイン」
次に、`FORALL`です。これは「複数の要素を独立して計算できる」ことをコンパイラに宣言するための構文です。
! 2次元配列の全要素にインデックスを使った演算を行う
FORALL (i = 1:N, j = 1:M)
A(i, j) = B(i, j) + C(j, i) ! 転置のようなアクセスも記述可能
END FORALL
ここが極限のポイント:DOループとの違い
初心者がよく陥る罠が「`FORALL`は単なる`DO`ループの省略形だ」という勘違いです。全く違います。
- DOループ:基本的には順次実行。前の計算結果を次のステップで使う(依存関係がある)処理に適している。
- FORALL:全要素が同時に計算されることを想定している。
コンパイラはこの「同時に計算しても安全」という保証を利用して、マルチコアCPUやGPUへ演算をオフロード(自動並列化)します。もし皆さんが`FORALL`の中で`A(i) = A(i-1) + 1`のような「前の値が必要な処理」を書くと、並列化が破壊されるか、あるいは予測不能な挙動を引き起こします。
「FORALLは独立した計算の集まりである」。このルールを死守してください。
—
3. 現場で生き残るための「メモリ配置」の鉄則
どれほど`WHERE`や`FORALL`を駆使しても、メモリのアクセス順序を間違えると全てが台無しになります。Fortranは「列優先(Column-major)」です。
! 高速:メモリ上で連続する領域を順に叩く
FORALL (j = 1:M, i = 1:N)
A(i, j) = …
END FORALL
逆に、内側のループを`j`にしてしまうと、メモリ上で飛び石状にアクセスが発生し、キャッシュミスが多発します。最新のCPUであっても、キャッシュラインの効率的な利用は演算速度を10倍以上変えます。「配列の左側の添字を高速に回す」。この泥臭い鉄則は、どんなに高度な構文を使っても変わりません。
—
4. コンパイラに「本気」を出させるビルド設定
最後に、実際にコードをビルドする際の魔法の言葉を授けます。Intel Fortran (`ifort`/`ifx`) を使う場合、以下のようなフラグを試してみてください。
現場での最適化例
ifx -O3 -xHost -qopenmp -parallel -qopt-report=5 source.f90
- `-O3`: 言語仕様の限界まで最適化を行う。
- `-xHost`: 実行環境のCPUアーキテクチャに特化した命令(AVX-512など)をフル活用する。
- `-parallel`: 自動並列化を有効にする。
- `-qopt-report=5`: コンパイラがどこをベクトル化し、どこで挫折したかを詳細なレポートで出力してくれる。(これを見ないと一人前とは言えません!)
—
最後に:皆さんへ
`WHERE`や`FORALL`を使うことは、単にコードを短くするためではありません。コンパイラという「超優秀な相棒」に対して、「ここは並列化しても安全だぞ」「ここはベクトル演算に最適だぞ」という明確な意図を伝えるコミュニケーションなのです。
最初は戸惑うこともあるでしょう。でも、その違和感こそが、皆さんが「ただ動くコード」から「物理現象を高速に再現するコード」へと成長している証です。
まずは小さな配列で、レポート出力を見ながら遊んでみてください。Fortranの世界へようこそ。一緒に計算の限界を突き詰めましょう!

コメント