【入門編】OpenMP並列ループにおけるPRIVATEとSHAREDのデータ共有制御 – モダンFortran言語仕様と実践実践マスター

Fortranで並列計算を極める:OpenMPの「見えない壁」を突破する作法

こんにちは。宇宙航空の現場で、長年スパコンの演算性能を限界まで引き出すことに心血を注いできた者です。

CやPythonからFortranの世界に飛び込んできた皆さん、ようこそ。Fortranは「古い」などと言われますが、こと「浮動小数点演算の純粋な速度」と「巨大な数値計算の安定性」においては、今なお最強の武器です。

今回は、並列処理の要である「OpenMP」において、若手が必ず一度は躓く「変数スコープの管理」と「偽共有(False Sharing)」について、現場の泥臭い知見を共有しましょう。

1. 変数の支配権:SHAREDとPRIVATEをどう使い分けるか

Fortranで並列ループを書くとき、`!$OMP PARALLEL DO`を記述しますよね。ここで最も重要なのが、変数を「みんなで共有する(SHARED)」か「各スレッドが個別に持つ(PRIVATE)」かの判断です。

基本の考え方

  • SHARED: 大規模なデータ(配列など)はメモリを浪費しないよう共有します。ただし、書き込みが競合すると「競合状態(Race Condition)」で計算が壊れます。
  • PRIVATE: ループ内の作業変数(一時的な計算量など)は各スレッドが持つべきです。これを共有すると、隣のスレッドが書いた値を上書きしてしまい、計算結果が毎回変わるという「デバッグ地獄」に直結します。

!$OMP PARALLEL DO SHARED(A, B) PRIVATE(i, temp)
do i = 1, N
temp = A(i) 2.0 ! 各スレッドが独立したtempを持つことで安全!
B(i) = temp + C ! Cは定数なのでSHAREDでOK
end do
!$OMP END PARALLEL DO

現場の格言: 「迷ったらまずはPRIVATE」。メモリ消費を恐れて全ての変数をSHAREDにすると、必ずどこかでスレッド間の殴り合いが発生します。

2. 誰も教えてくれない「偽共有 (False Sharing)」の罠

ここからが本題です。コードは正しく書けているはずなのに、なぜか並列数を増やしても速度が頭打ちになる……そんな経験はありませんか?

CPUのキャッシュは「キャッシュライン」という単位(通常64バイト)でメモリを管理しています。もし、「別々のスレッドが扱う変数が、たまたま同じキャッシュライン上に並んでいる」とどうなるか。

CPUは「キャッシュの整合性を保たなきゃ!」と、お互いのキャッシュを無効化し合う「キャッシュコヒーレンシ・トラフィック」を発生させます。これを偽共有と呼びます。論理的には並列なのに、物理的にCPU同士が足を引っ張り合っている状態です。

偽共有を回避するデータ配置戦略

例えば、各スレッドが書き込むためのカウンタを配列で持たせる場合、以下のように書いてはいけません。

! 【ダメな例】密接した配列要素をスレッドが同時に書き換える
integer :: thread_counts(MAX_THREADS)
!$OMP PARALLEL DO
do i = 1, MAX_THREADS
thread_counts(i) = thread_counts(i) + 1 ! 隣り合うメモリに同時にアクセスが発生!
end do

これを防ぐには、「パディング(詰め物)」を入れます。

! 【改善例】要素間にダミーを挟み、別のキャッシュラインに追い出す
integer :: thread_counts(8, MAX_THREADS) ! 8個分ずらしてキャッシュラインを分ける
!$OMP PARALLEL DO
do i = 1, MAX_THREADS
thread_counts(1, i) = thread_counts(1, i) + 1
end do

このように、物理的なメモリ配置を意識することで、ハードウェアの性能を「素直に」引き出すことができます。

3. 実践:ビルドとパフォーマンス計測の作法

最後に、コンパイル時の最適化フラグについても少しだけ触れておきましょう。

Gfortranでコンパイルする際の推奨例
gfortran -O3 -fopenmp -march=native -ffast-math -o solver.exe main.f90

  • `-O3`: Fortranの最適化は、コンパイラに「好きに料理してくれ」と任せるのが一番です。
  • `-march=native`: 今使っているCPUの命令セット(AVX2やAVX-512)をフル活用させます。
  • `-ffast-math`: 浮動小数点の厳密な結合法則を無視して演算順序を並び替えます。数値計算の精度と速度のトレードオフですが、物理シミュレーションでは必須です。

終わりに:数値計算は「実験」である

Fortranのプログラミングは、単なるコード記述ではなく、ハードウェアへの「物理的な指令」です。

並列化がうまくいかないときは、コードを眺めるだけでなく、`perf`コマンドなどのプロファイラを使って「どこでキャッシュミスが起きているか」を観察してください。最初は難しく感じるかもしれませんが、キャッシュラインの動きを想像できるようになれば、あなたはもう一人前の数値計算アーキテクトです。

まずは小さなループから、変数スコープを意識して書き始めてみてください。応援していますよ!

コメント

タイトルとURLをコピーしました