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

C言語のメモリをFortranで「魔改造」する:C_F_POINTERの極意

こんにちは。元宇宙航空研究機関で、数テラバイトの流体シミュレーションと格闘してきた者です。

C言語やPythonを触ってきた皆さんがFortranの世界へ足を踏み入れると、最初にぶつかる壁が「メモリ管理」です。Fortranは本来、コンパイラがメモリを自動管理する「お行儀の良い」言語ですが、大規模な数値計算の現場では、「C言語で確保した巨大なデータ領域を、Fortranの高速な配列として直接叩きたい」という切実な要求が必ず発生します。

そこで登場するのが `ISO_C_BINDING` モジュールの `C_F_POINTER` です。これ、単なる「型変換」だと思ったら大間違い。CPUのキャッシュ効率とFortranの最適化能力を最大限に引き出すための、極めて重要な儀式なんです。

なぜ「コピー」してはいけないのか?

まず大前提ですが、Cで作った巨大配列をFortran側に渡すとき、要素ごとにコピーを作成するのは「禁じ手」です。メモリの無駄ですし、何よりキャッシュミスが多発して計算速度が劇的に落ちます。

我々がやりたいのは、「Cが確保したメモリの住所(ポインタ)を、Fortranの配列として名前を付けてあげる」という、メモリへの「ラベル貼り」のような操作です。

1. 魔法の呪文 `ISO_C_BINDING` を召喚する

まずは、Cとの架け橋となるモジュールを呼び出しましょう。これがなければ始まりません。

module memory_bridge
use, intrinsic :: iso_c_binding
implicit none

contains
subroutine map_c_memory(c_ptr, n)
type(c_ptr), value :: c_ptr ! Cからのポインタを受け取る
integer(c_int), value :: n ! 配列のサイズ

! Fortran側の配列ポインタを定義
real(c_double), pointer :: f_array(:)

! 魔法の関数:CのポインタをFortranの配列にキャストする
call c_f_pointer(c_ptr, f_array, [n])

! ここからf_arrayは、Cのメモリを直接参照するFortran配列として振る舞う
! 読み書きは高速なFortranの最適化が適用される
f_array(1) = 1.0d0
end subroutine
end module

2. 泥臭い注意点:列優先順位(Column-Major)の罠

ここで一つ、現場のエンジニアが必ずハマる「地雷」についてお話しします。

Fortranは「列優先(Column-Major)」です。一方でC言語は「行優先(Row-Major)」。ここを理解せずに配列をマッピングすると、計算結果が壊滅的なことになります。

  • Cのメモリ配置: `a[0][0], a[0][1], a[0][2]…` (行が先に変わる)
  • Fortranのメモリ配置: `a(1,1), a(2,1), a(3,1)…` (列が先に変わる)

もし多次元配列(行列など)をマッピングするなら、C言語側の配列構造と、Fortran側の添字の持ち方を完全に一致させる必要があります。もしCで `data[N][M]` と確保しているなら、Fortran側では `real(c_double), pointer :: f_array(:, :)` と定義し、`shape`引数で `[M, N]` と指定してあげる工夫が必要です。

3. 最適化のための「極限の知見」

現場で生き残るために、以下の3点を心に刻んでおいてください。

1. Contiguous(連続性)の保証:
`C_F_POINTER` でマッピングした配列は、Fortran側から見て「メモリが連続している」とコンパイラが認識すると、SIMDベクトル化(CPUの並列演算機能)が一気に加速します。コードを書く際は、できるだけフラットな1次元配列として扱い、インデックス計算を工夫する方がトラブルが少ないです。

2. 型の一致:
Cの `double` はFortranの `real(c_double)`、`int` は `integer(c_int)`。ここをケチって適当な型を当てると、メモリのアライメント(境界調整)がズレて、計算速度が1/10になるどころか、セグメンテーション違反でプログラムが死にます。必ず `iso_c_binding` の定数を使ってください。

3. デバッグの基本は「アドレス確認」:
もし計算がおかしいときは、`c_loc()` を使ってFortran変数のアドレスを確認し、C言語側のポインタ値と比較してください。一致していなければ、それは「マッピングの失敗」です。

まとめ:まずはここから

まずは小さなプログラムで、C言語側で確保した `malloc` 領域をFortranの配列に流し込み、値を書き換えてみてください。

Fortranは、古い言語だと思われがちですが、メモリの直叩きができる最強の「高速計算エンジン」です。この `C_F_POINTER` を使いこなせるようになれば、既存のC資産を活かしつつ、計算コアだけをFortranで爆速化させるという、エンジニアとしての強力な武器が手に入ります。

「計算が遅い」「メモリが足りない」と悩む夜からおさらばするために、ぜひ今日から導入してみてください。応援していますよ!

コメント

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