導入
数値計算プログラムの開発において、グリッド生成や定数テーブルの読み込みといった「一度だけ実行すればよい前処理」の管理に頭を悩ませることはありませんか。モジュール変数とSAVE属性を適切に利用することで、計算の安定性を高めつつ、コードの可読性を劇的に向上させることができます。本記事では、モジュール変数の初期化挙動を理解し、安全なシングルトンパターンを実装する方法を解説します。
基礎知識
Fortranのモジュール内で宣言された変数は、プログラムの実行開始時に一度だけメモリに確保され、初期化されます。特にSAVE属性を付与した変数は、プログラム終了までその状態が保持されるため、サブルーチンを跨いでも値を維持できます。この性質を応用することで、特定の処理が「実行済みかどうか」をフラグで管理し、二重実行を防ぐ仕組みを構築できます。これは、オブジェクト指向におけるシングルトンパターンに相当する考え方です。
実装/解決策
具体的なアプローチは、論理型のフラグ変数を用意し、モジュール内の初期化ルーチンでそのフラグをチェックすることです。
1. モジュールレベルで `logical, save :: is_initialized = .false.` を宣言します。
2. 初期化が必要なルーチンの先頭で、このフラグを確認します。
3. フラグが `.false.` なら初期化処理を実行し、直後に `.true.` に書き換えます。
これにより、メインプログラム側で初期化のタイミングを意識することなく、必要なルーチンを呼び出すだけで安全に前処理が完了します。
サンプルプログラム
以下のコードは、グリッド生成を一度だけ実行する構成例です。
module grid_manager
implicit none
! 初期化フラグ:SAVE属性により状態が保持される
logical, save :: is_initialized = .false.
real, allocatable :: grid_points(:)
contains
subroutine initialize_grid(n)
integer, intent(in) :: n
! 初期化済みであれば処理をスキップ(未定義動作の回避)
if (is_initialized) return
! 初回のみ実行される重い処理
allocate(grid_points(n))
grid_points = [(real(i), i=1, n)]
is_initialized = .true.
print , "グリッドの初期化が完了しました。"
end subroutine initialize_grid
end module grid_manager
! メインプログラム
program main
use grid_manager
call initialize_grid(10)
call initialize_grid(10) ! 二度目は何も起こらない
end program main
応用・注意点
この手法を用いる上で注意すべき点は、マルチスレッド環境(OpenMP等)での競合です。複数のスレッドから同時に `initialize_grid` が呼び出されると、フラグのチェックと書き換えのタイミングで競合が発生し、初期化が複数回走る可能性があります。並列環境で使用する場合は、`!$omp atomic` や `!$omp critical` を用いて、フラグの更新処理を排他制御してください。また、モジュール変数はグローバルな寿命を持つため、メモリを大量に消費する配列を扱う際は、不要になったタイミングで `deallocate` を行うなど、リソース管理にも気を配るのが実務的なエンジニアの作法です。

コメント