1. 導入:なぜループ処理での書き出しは「悪」なのか
数値計算の現場では、テラバイト級の巨大な配列をシミュレーション結果として保存することが日常茶飯事です。この際、初心者が陥りがちなのが「doループで要素を一つずつ書き出す」という実装です。要素ごとの書き出しは、OSのシステムコールを無駄に呼び出し、ディスクI/Oのオーバーヘッドを増大させます。本記事では、Fortranにおける「一括I/O(バースト転送)」を活用し、I/O時間を劇的に短縮する技術を解説します。
2. 基礎知識:バースト転送とメモリ配置
計算機科学における「バースト転送」とは、連続したメモリ領域を一度の命令で転送する仕組みのことです。Fortranの `write(unit) array` という構文は、コンパイラに対して「配列のメモリ上の先頭アドレスからサイズ分だけ、そのままストレージへ流し込め」という指示を出します。これにより、CPUはI/O待ちのループから解放され、DMA(Direct Memory Access)コントローラがメモリとディスク間を直接制御するため、ハードウェアの理論帯域に近い速度で書き込みが可能になります。
3. 実装の考え方
重要なのは「配列をメモリ上で連続させること」と「バイナリ形式で出力すること」です。Fortranでは配列は列優先(Column-major order)でメモリに配置されます。多次元配列であっても、メモリ上で連続した領域として確保されていれば、一つの `write` ステートメントで全要素を一括出力可能です。
4. サンプルプログラム:一括I/Oの実行例
以下は、3次元配列をバイナリファイルへ高速に書き出し、読み込むためのサンプルコードです。
program fast_io_example
implicit none
integer, parameter :: nx = 1000, ny = 1000, nz = 100
real(8), allocatable :: data(:,:,:)
integer :: unit_num, ierr
! 配列のメモリ確保
allocate(data(nx, ny, nz))
data = 1.0d0 ! 適当な初期値
! バイナリファイルとして開く
open(newunit=unit_num, file='output.bin', status='replace', access='stream')
! 【重要】一括I/Oの実行
! ループを使わず、配列名のみを指定することでバースト転送を促す
write(unit_num) data
close(unit_num)
print , "大規模配列の書き出しが完了しました。"
deallocate(data)
end program fast_io_example
5. 応用・注意点:現場での落とし穴
現場でこの手法を用いる際、以下の点に注意してください。
・ファイル形式の互換性
`write(unit) array` はFortran独自のバイナリ形式(アンフォーマット)です。異なる言語(Pythonなど)から読み込む場合は、`access=’stream’` を指定した上で、`read(unit) array` ではなく、バイナリリーダを用いる必要があります。
・配列の非連続アクセス(ストライド)
もし配列の一部だけを `write` しようとして `data(1:100:2, :, :)` のようなスライス指定を行うと、メモリが不連続になるため、コンパイラは内部で一時バッファへのコピー(パッキング)を行います。これが発生するとI/O効率が落ちるため、可能な限り「連続したメモリブロック」として書き出せるようデータ構造を設計するのがベストです。
・ディスクのボトルネック
I/Oが高速化されると、今度はディスクの物理的な書き込み速度が限界になります。大規模計算では、並列I/O(MPI-IOなど)の検討も視野に入れましょう。まずはこの「一括I/O」を徹底するだけで、多くのケースで数倍〜数十倍の高速化が期待できます。

コメント