padding させないためには、sequence か bind(c) か?
派生型変数を一括書き出しするとき、データのバイト数が中途半端だとコンパイラが適当に並べ直したり、padding してデータ境界を調整しますが、それをさせずにあくまで宣言したとおりの順序と大きさで並べて欲しい時もあります。(データフォーマットの決まったバイナリ・ファイルの書き出しとか)
今まで sequence を使ってきましたが、コンパイラによっては並びの順序は変えないものの padding はするようなものもあるような話は聞いていました。
最近の Dr. Fortran のブログ記事を読んで sequence がいいのか bind(c) がいいのか気になってきたので確かめてみました。 stevelionel.com
結論から言うと、gfortran はどちらも宣言通り、intel fortran では sequence は宣言通りで、bind(c) は padding するでした。つまり sequence にすればよろしいあるヨ。
program test implicit none c : block use, intrinsic :: iso_c_binding type, bind(c) :: c_t character(c_char) :: c = 'c' integer(c_int16_t) :: i1 = 12345 integer(c_int64_t) :: i2 = 123456789 end type c_t type(c_t) :: ct integer :: n inquire(iolength = n) ct ! print *, c_sizeof(ct), sizeof(ct) print *, 'bind_c iolength =', n, 'bytes', storage_size(ct) / 8, 'bytes' open(9, file='bindc.9', access='stream', status='replace') write(9) ct close(9) end block c f : block use, intrinsic :: iso_fortran_env type :: f_t sequence character :: c = 'f' integer(int16) :: i1 = 12345 integer(int64) :: i2 = 123456789 end type f_t type(f_t) :: ft integer :: n inquire(iolength = n) ft print *, 'sequence iolength =', n, 'bytes', storage_size(ft) / 8, 'bytes' open(9, file='bindf.9', access='stream', status='replace') write(9) ft close(9) end block f end program test
intel fortran は I/O の record length が word 単位なので -assume byterecl のコンパイル時オプションで単位を byte に変えてやる必要があります。
$ gfortran bindc.f90 $ ./a.out bind_c iolength = 11 bytes 16 bytes sequence iolength = 11 bytes 16 bytes $ ifort -assume byterecl bindc.f90 bindc.f90(24): 警告 #6379: 構造体は 1 つ以上のアライメントされていないフィールドを含んでいます。 [F_T] type :: f_t ----------------^ $ ./a.out bind_c iolength = 16 bytes 16 bytes sequence iolength = 11 bytes 11 bytes
Modern Fortran: Building efficient parallel applications
- 作者:Curcic, Milan
- 発売日: 2020/10/06
- メディア: ペーパーバック
Modern Fortran: Style and Usage
- 作者:Clerman, Norman S
- 発売日: 2012/02/23
- メディア: ペーパーバック