fortran66のブログ

fortran について書きます。Amazonのアソシエイトとして収入を得ています。

【メモ帳】Linux の RAM Disk /dev/shm

execute_command_line からの python 実行時に RAM Disk 利用

昔、Fortran 2003 で導入された execute_command_line の利用例として、Fortran の中で Python スクリプトを書いてファイル出力した後、それを実行すると言うことをやりました。

fortran66.hatenablog.com

その時は Fortran と Python でのデータのやり取りを、直接スクリプト内に書き込む方式でやりましたが、numpy があれば Fortran 形式のバイナリファイルを読み書きできるので、ファイルを介したデータやり取りが比較的効率的にできます。この時、Linux だとお仕着せの RAM Disk があると言うことを AI に教えられたので、それを利用してみることにします。MacOS / Windows には残念ながらデフォルト状態で使える仕組みはないようです。

この方式は、一旦外部ファイルに書き出すのでダサい感はあるのですが、DLL とか Link path とかを気にしなくていいので、コロコロ環境が変わる Python に対するロバスト性というか、打たれ強さがあるところが利点です。また、スクリプトを内部生成するので、ファイルが Fortran プログラムだけで済むことも気楽です。ただ write 文で書き出すのが色々だるいところがありますが、今は AI 小僧に書かせればいいので昼下がりの気だるさ位で済みます。

実行結果

配列を外部ファイルに書き出して、Python に処理させた後、また配列に読み込みます。

(pyfort) $ ./a.out
 RUN: python /dev/shm/pyfor.py /dev/shm/fort_in.bin /dev/shm/fort_out.bin 10
Python read: [ 1.  2.  3.  4.  5.  6.  7.  8.  9. 10.]
 Python exit status =            0
 Fortran got back:
   10.500000000000000        20.500000000000000        30.500000000000000        40.500000000000000        50.500000000000000        60.500000000000000        70.500000000000000        80.500000000000000        90.500000000000000        100.50000000000000

ソース・プログラム

Linu ではファイル位置を /dev/shm 下におくと RAM Disk が使われるそうなので、少しだけ外部ファイル経由が軽くなります。WSL2 だとそれで動くけれど、メモリ管理の複雑さから色々微妙な模様です。

なお CLOSE 文に status = 'DELETE' 属性をつけることで、テンポラリのデータファイルや Python スクリプトを消去しています。

program pytest
    use iso_fortran_env, only : real64
    implicit none

    integer, parameter :: n = 10
    real(real64) :: arr(n), arr2(n)
    integer :: i, u, ios, exitstat
    character(len=*), parameter :: fin  = '/dev/shm/fort_in.bin'
    character(len=*), parameter :: fout = '/dev/shm/fort_out.bin'
    character(len=*), parameter :: pys  = '/dev/shm/pyfor.py'
    character(len=512) :: cmd

    do i = 1, n
        arr(i) = real(i, real64)
    end do

    !--- make data file
    open(newunit=u, file=fin, access='stream', form='unformatted', &
         status='replace', action='write', iostat=ios)
    if (ios /= 0) stop 'cannot open input binary'
    write(u) arr
    close(u)

    !--- generate Python script 
    open(newunit=u, file=pys, status='replace', action='write', iostat=ios)
    if (ios /= 0) stop 'cannot open python script'

    write(u,'(A)') 'import sys'
    write(u,'(A)') 'import numpy as np'
    write(u,'(A)') ''
    write(u,'(A)') 'fin  = sys.argv[1]'
    write(u,'(A)') 'fout = sys.argv[2]'
    write(u,'(A)') 'n    = int(sys.argv[3])'
    write(u,'(A)') ''
    write(u,'(A)') 'arr = np.fromfile(fin, dtype=np.float64, count=n)'
    write(u,'(A)') 'print("Python read:", arr)'
    write(u,'(A)') 'arr = arr * 10.0 + 0.5'
    write(u,'(A)') 'arr.tofile(fout)'
    close(u)

    !--- Python Execution
    !
    cmd = 'python ' // pys // ' ' // fin // ' ' // fout // ' 10'
    print *, 'RUN: ', trim(cmd)

    call execute_command_line(trim(cmd), exitstat=exitstat)
    print *, 'Python exit status = ', exitstat
    if (exitstat /= 0) stop 'python failed'

    !--- read results
    open(newunit=u, file=fout, access='stream', form='unformatted', &
         status='old', action='read', iostat=ios)
    if (ios /= 0) stop 'cannot open output binary'
    read(u) arr2
    print *, 'Fortran got back:'
    print *, arr2

    ! delete files
    close(u, status = 'delete')

    open(newunit = u, file = fin)
    close(u, status = 'delete')

    open(newunit = u, file = pys)
    close(u, status = 'delete')
end program pytest