トップ «前の日記(2008-02-27) 最新 次の日記(2008-03-05)» 編集

Orz日記 by Akio Morita

ToDo:

  • 15 SAD Fit[]回りの障害事例の解析
  • 10 smart pointer版PEGクラスの再実装(Left Recursionまわり)
2006|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|06|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|07|08|09|10|11|12|
2013|01|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|06|07|08|10|12|
2016|01|02|03|05|06|08|10|11|
2017|01|02|03|04|05|06|07|09|10|11|12|
2018|01|02|03|04|06|07|08|09|10|11|12|
2019|01|03|04|05|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|

2008-02-27 [長年日記]

_ [SAD][Fortran]Fortran I/Oの怪

Fortran90から入った停留入力で制御文字"\\r"の振るまい奇妙なので、 実装ごとの挙動を調べてみました。

ここでは、"\\r","\\n","\\r\\n"などの改行記号が 入力列に含まれて際の read文からの戻り値を調べています。 サンプルとした入力列は、"abc\\rdef\\n\\nABCDEF\\r\\n123456\\neof"です。 この入力列は、標準入力(UNIT=5)とファイル経由(UNIT=10)で テストプログラムに入力され、read文で停留入力による読み取りが行われます。

g95 0.91 20080220の場合

標準入力"abc\\rdef",size=7,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
ファイル"abc\\rdef",size=7,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
  • 標準入力とファイル経由の結果は同じ
  • 単独の"\\r"は1文字として扱う
  • "\\r\\n"と"\\n"は改行記号扱いで区別できない
  • 複数文字を受け取る場合、改行記号とEOFを区別できない
    • 1文字づつ受け取る場合は、EOFを検知する直前が改行記号で無い場合には、size=0,iostat=-2が返って来る
    • この例だと、"e",size=1,iostat=0->"o",size=1,iostat=0->"f",size=1,iostat=0->size=0,iostat=-2->EOF検知

gfortran 4.2.4 20080220 (prerelease)の場合

標準入力"abc",size=3,iostat=-2"ef",size=2,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
ファイル"abc",size=3,iostat=-2"def",size=3,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
  • 標準入力からだと"\\r"の次が"\\n"以外の文字の場合に欠落する
    • CR-LFを仮定して LFを先読みした後に seekで戻しているが、un-seek-ableなストリームではこの実装はうまく動かない
  • "\\r\\n"、"\\r"と"\\n"は改行記号扱いで区別できない
  • 複数文字を受け取る場合、改行記号とEOFを区別できない
    • 1文字づつ受け取る場合は、改行記号とEOFを区別できる
    • この例だと、"e",size=1,iostat=0->"o",size=1,iostat=0->"f",size=1,iostat=0->EOF検知

gfortran 4.3.0 20080221 (prerelease)の場合

標準入力"abc",size=3,iostat=-2"ef",size=2,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
ファイル"abc",size=3,iostat=-2"def",size=3,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
  • 標準入力からだと"\\r"の次が"\\n"以外の文字の場合に欠落する
    • CR-LFを仮定して LFを先読みした後に seekで戻しているが、un-seek-ableなストリームではこの実装はうまく動かない
  • "\\r\\n"、"\\r"と"\\n"は改行記号扱いで区別できない
  • 複数文字を受け取る場合、改行記号とEOFを区別できない
    • 1文字づつ受け取る場合は、改行記号とEOFを区別できる
    • この例だと、"e",size=1,iostat=0->"o",size=1,iostat=0->"f",size=1,iostat=0->EOF検知
  • gfortran 4.2.4 20080220 (prerelease)と同じ動作

Intel Fortran Compiler Version 8.1の場合

標準入力"abc\\rdef",size=7,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
ファイル"abc\\rdef",size=7,iostat=-2"",size=0,iostat=-2"ABCDEF",size=6,iostat=-2"123456",size=6,iostat=-2"eof",size=3,iostat=-2
  • 標準入力とファイル経由の結果は同じ
  • 単独の"\\r"は1文字として扱う
  • "\\r\\n"と"\\n"は改行記号扱いで区別できない
  • 複数文字を受け取る場合、改行記号とEOFを区別できない
    • 1文字づつ受け取る場合は、EOFを検知する直前が改行記号で無い場合には、size=0,iostat=-2が返って来る
    • この例だと、"e",size=1,iostat=0->"o",size=1,iostat=0->"f",size=1,iostat=0->size=0,iostat=-2->EOF検知

まとめ

  • "\\r\\n"と"\\n"が同一視されている
  • 2008/02/20頃のgfortran 4.2.4/4.3.0実装は明らかにおかしい
    • 先行するCRに対応するLFを一文字読み込みして探すのは良いとして、LFで無かった際に正しくストリームを巻戻せず入力を失っている(標準入力)
  • 改行記号終端でない入力でのEOF検知の挙動が実装で異なる
    • 改行記号によるレコード終端の取り扱いに差か?
    • gfortranは、EOFによる暗黙のレコード終端よりEOF検知が優先している
    • intel fortran/g95は、EOFによる暗黙のレコード終端によって一度、size=0,iostat=-2(改行〜レコード終端)を返してから、EOFを検知する

つまり、改行区切りなテキスト読み込み(itfgetbuf_)には使えるが、uni-byte stream入力には使えないと言うこと

性能比較

itfgetbuf_の実装を

  • Q editing
  • FGETC intrinsic
  • Fortran90 停留入力
  • read(2) system call

で行ったものを比較すると、read(2)が断トツに遅いのは予定通りだとしても、 停留入力がFGETCより遅いのは納得がいかないな

Intel Fortran 8.1で試すと、処理時間比は Q editing:FGETC:停留入力:read(2)で1:1.7:7:23ぐらいで、 FGETCに比べるて停留入力が4倍近く遅い... Orz

gfortranだと、FGETC:停留入力:read(2)で 1:1.6:8.9ぐらいでそれほど激しい差では無いですが停留入力の方が遅い

内容的には、I/O library側でループを組める&最適化できるので FGETCのループより早くてもよさそうなモノですが...

itfgetbuf_の実装に限れば、

  1. Q編集記述子が有るならそれを使え
  2. さもなくば、FGETC intrinsicを使え
  3. 他に手が無ければ停留入力で書け
  4. read(2)は最後の武器だ

ということですね。 実際は、binary Read[]で FGETC intrinsicが使われているので FGETCが未実装の場合、整合性を取ろうとするとread(2)で 全部実装するしかないのですが... (それでも、Write[]とFileSeek[]との完全には整合性しない)


カテゴリー: Admin | Emacs | EPICS | Fortran | FreeBSD | GCC | hgsubversion | IPv6 | KEKB | LHC | Lisp | LLVM | MADX | Ryzen | SAD | samba | tDiary | unix | WWW | YaSAI | お仕事 | イベント | 出張 | 宴会 | 数学 | 艦これ | 買いもの | 追記 | 雑記