【実務・中級編】C_F_POINTERによるC言語側で確保したメモリのFortran配列化 – モダンFortran言語仕様と実践実践マスター

C言語のメモリをFortranで支配する:`C_F_POINTER` 徹底攻略

大規模数値計算の現場では、時として「C言語で確保した巨大なデータ領域を、Fortranの高速な多次元配列として扱いたい」という要求に直面します。例えば、GPUメモリ管理や、Python(NumPy)経由でのデータ受け渡し、あるいは既存のレガシーなCライブラリとの連携です。

ここで `c_f_pointer` を安易に使うと、デバッグ地獄が待っています。今回は、単なる構文解説ではなく、コンパイラの最適化性能を最大限に引き出し、かつメモリリークやセグメンテーションフォールトを回避する「堅牢な設計」を伝授します。

1. `C_F_POINTER` の本質と「アライメント」の罠

`c_f_pointer` は魔法ではありません。単にメモリアドレスをFortranのポインタ記述子(Descriptor)に関連付けるだけの薄いラッパーです。ここで最も重要なのは、「Fortran側が期待するメモリアライメント」をC側が保証しているかという点です。

モダンなCPUは、128bitや256bitの境界でアライメントされたメモリに対して、SIMD命令(AVX-512等)を用いたベクトル化を行います。もしC側で単なる `malloc` を使い、奇数バイトから始まるポインタを `c_f_pointer` に渡すと、コンパイラはベクトル化を諦めるか、あるいは実行時に「アライメント不正」でクラッシュします。

鉄則: C側では必ず `posix_memalign` や `_mm_malloc` を使い、少なくとも64バイト境界でメモリを確保してください。

2. 実装パターン:型安全なマッピング

以下は、Cで確保した領域をFortranの多次元配列として安全にキャストし、最適化フラグを最大限に享受するための実装テンプレートです。

module memory_bridge
use, intrinsic :: iso_c_binding
implicit none
private
public :: map_c_to_fortran

contains

subroutine map_c_to_fortran(c_ptr, dims, fortran_array)
! Cのポインタと形状からFortranのポインタ配列を生成する
type(c_ptr), intent(in) :: c_ptr
integer(c_size_t), intent(in) :: dims(:) ! 配列の形状
real(c_double), pointer, intent(out) :: fortran_array(:, 🙂 ! 多次元配列として定義

! コンパイラ最適化の鍵:target属性を持つポインタへの紐付け
call c_f_pointer(c_ptr, fortran_array, dims)
end subroutine map_c_to_fortran
end module memory_bridge

3. パフォーマンスを殺さないための「列優先」の意識

Fortranはメモリ配置が「列優先(Column-major)」です。Cは「行優先(Row-major)」です。
もしC側から持ってきたメモリをFortranで扱う際、ループの入れ子順序をCの感覚(外側が行、内側が列)で書くと、キャッシュミスが多発し、計算速度が10倍以上低下します。

  • Bad: `do j = 1, N; do i = 1, M; A(i, j) = …` (行方向へのアクセスはFortranでは非連続)
  • Good: `do j = 1, N; do i = 1, M; A(i, j) = …` (Fortranではこれがメモリ上を連続して走る)

`c_f_pointer` でマッピングする際は、C側で確保した配列のインデックス順序を逆転させてFortranの形状指定に渡すのがプロの流儀です。

4. 最適化を極限まで引き出すビルド構成

いくらコードを綺麗に書いても、コンパイラに「この配列はエイリアス(別名)を持たない」と教えなければ、コンパイラは安全側に倒してベクトル化を抑制します。

Intel Fortran (ifort/ifx) や gfortran を使う場合、以下のフラグを必ず検討してください。

  • `-fno-alias` / `-fno-f2c`: Fortran配列がメモリ上で他と重ならないことをコンパイラに保証します。
  • `-qopt-report` (Intel): どのループがベクトル化され、どのループが拒否されたかを詳細なログで確認してください。ここを読まずして数値計算を語ることはできません。

シニアからの提言:所有権の明確化

最後に、最も重要なのは「誰がそのメモリを解放するのか」という所有権の問題です。`c_f_pointer` で紐付けたFortran配列は、決して `deallocate` してはいけません。

必ずC側の解放関数(`free` など)を呼ぶまで、Fortran側では「単なる参照」として扱うこと。これを規約(Contract)としてコードベース内に明記してください。バグの多くは、言語の仕様ではなく、こうした「所有権の曖昧さ」から生まれます。

現場のコードは、美しさよりも「故障しないこと」と「キャッシュに優しいこと」が全てです。この実装テンプレートをベースに、あなたのシミュレーションエンジンのエンジンをより強固なものに仕上げてください。質問があれば、またいつでもどうぞ。

コメント

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