入出力性能の向上

全体的な入出力性能を向上させることで、デバイス入出力と実際の CPU 時間の両方を最小限に抑えることができます。ここに示すテクニックは、多くのアプリケーションの性能を大幅に向上させることができます。

入出力文は、実行プログラムで最も遅いプロセスに属し、プログラム実行の最大速度を制限します。一部のプログラムでは、入出力が実行時性能の向上を妨げるボトルネックとなっています。入出力の問題を解消するための鍵は、入出力に関連する CPU 時間と入出力デバイス時間を減らすことです。

この問題は、以下の 1 つまたは複数の原因により発生します。

コーディングを改善することで、実際のデバイス入出力と実際の CPU 時間を最小限に抑えることができます。インテルは、デバイス入出力の遅延を最小限に抑えるなど、システム全体の問題に対応するソフトウェア・ソリューションを提供します。

書式付きファイルの代わりに書式なしファイルを使用する

書式なしファイルを可能な限り使用してください。数値データの書式なし入出力は、書式付き入出力よりも効率的で高精度です。ネイティブ書式なしデータは、転送の際に変更する必要がなく、外部ファイルにおける占有空間が小さくなります。

一方、書式付きファイルにデータを書き出すときには、書式付きデータを出力のために文字列に変換しなければならず、1 回の操作で転送できるデータ量が減ります。また、書式付きデータを再び 2 進形式として読み取ると、精度が失われることがあります。

次に示す、配列 A(25,25) を書き出す文は、S1 の方が S2 よりも効率的です。

S1         WRITE (7) A
S2         WRITE (7,100) A
100   FORMAT (25(' ',25F5.21))

書式付きデータファイルは、他のシステムへの移植がより簡単ですが、インテル® Fortran は書式なしデータをいくつかの書式に変換することができます (「リトル・エンディアン - ビッグ・エンディアンの変換 (IA-32 アーキテクチャー)」を参照)。

配列または文字列全体を書き出す

不要なオーバーヘッドをなくすために、個々の要素を何度も書き出すのではなく、配列または文字列全体を一度に書き出すようにしてください。入出力リスト中の各項目は、それぞれ独自の呼び出し手順を生成します。この処理のオーバーヘッドは、DO 形ループで最も大きくなります。配列全体を参照するときには、DO 形ループを使う代わりに配列名 (Fortran 配列構文) を使用してください。

自然な記憶順で配列データを書き出す

可能な限り自然な昇順記憶を行ってください。自然な記憶順とは、一番左の添字が最も急速に 1 ずつ変化する列優先順です (「効率的な配列アクセス」を参照)。プログラムがこれ以外の順序でデータの読み書きを行っていると、効率的なブロック移動ができなくなります。

全体配列を書き出さない場合、この自然な記憶順が最も効率的な順序です。

不自然な記憶順を使用しなければならないとき、場合によっては、データをメモリーに転送し、データの順序を変更してから入出力操作を実行したほうが効率的なことがあります。

中間結果にメモリーを使用する

中間結果を周辺機器のファイルではなくメモリーに格納することで、性能が向上することがあります。逆に中間的な格納領域を使うことがかえって不利になる状況の 1 つは、データがシステムの物理メモリーと比べて格段に大きい場合です。このような場合、ページフォルトが頻繁に起こって、仮想メモリーの性能を大幅に低下させる可能性があります。

Linux* : ページフォルトによるシステムの CPU 性能の低下を特に憂慮する場合、コードにより使用されるファイルの保持には、メモリー・ファイル・システム (mfs) 仮想ディスクを使用することを検討してください。

DO 形ループコラプスを有効にする

DO 形ループコラプスは、入出力処理の大きなオーバーヘッドを軽減します。通常は、入出力リスト中の個々の要素が、インテル® Fortran ランタイム・ライブラリー (RTL) への呼び出しを個別に生成します。これらの呼び出し処理が、DO 形ループで最も大きなオーバーヘッドとなります。

インテル® Fortran は、最大 7 レベルまで入れ子された DO 形ループを、最適化されたランタイム・ライブラリー入出力ルーチンへの 1 回の呼び出しに置き換えることで、DO 形ループ中での呼び出し回数を減らします。このルーチンは多数の入出力要素を一度に転送することができます。

ループコラプスは、書式付き入出力と書式なし入出力のどちらでも可能ですが、いくつかの条件が満たされている必要があります。

VOLATILE 属性と文については、「Language Reference」(英語) を参照してください。

可変書式の式を使用する

可変書式の式 (インテル® Fortran 拡張) は実行時の書式指定とほぼ同じ柔軟性を備えていますが、コンパイラーが入出力書式を実行時に解析しなくて済むので、より効率的です。実行時には、少量の処理を行い、実際にデータ転送を行うだけで済みます。

一方、実行時の書式指定は性能を大幅に低下させる可能性があります。例えば、次の文では、書式指定が実行時にではなくコンパイル時に一度だけ行われる S1 の方が S2 よりも効率的です。

S1        WRITE (6,400) (A(I), I=1,N)
400 
 FORMAT (1X, <N> F5.2)
...
S2        WRITE (CHFMT,500) '(1X,',N,'F5.2)'
500 
 FORMAT (A,I3,A)
WRITE (6,FMT=CHFMT) (A(I), I=1,N)

レコードバッファーとディスク入出力を効率的に使用する

読み取りまたは書き出しが行われるレコードは、ユーザーのプログラムバッファーと、インテル® Fortran RTL によってファイルが開かれるときに設定される 1 つまたは複数のディスクブロック入出力バッファーの間で転送されます。読み取りまたは書き出されるレコードが極めて大きい場合を除き、ディスク読み書きの際には、ディスクブロック入出力バッファーに複数の論理レコードをバッファーして、物理ディスク入出力の回数が最小限に抑えられます。

ディスクブロック物理入出力バッファーの大きさは、OPEN 文の BLOCKSIZE 指定子を使って指定することができます。FORT_BLOCKSIZE 環境変数でデフォルトの BLOCKSIZE 値を変更できます。BLOCKSIZE 指定子を OPEN 文で省略し、FORT_BLOCKSIZE 環境変数も設定しなかった場合、128 キロバイトのデフォルトサイズが使用されます。

OPEN 文の BUFFERCOUNT 指定子は、入出力バッファーの数を指定します。BUFFERCOUNT のデフォルト値は 1 です。FORT_BUFFERCOUNT 環境変数でデフォルトの BUFFERCOUNT 値を変更できます。

OPEN 文に BLOCKSIZE および BUFFERCOUNT 指定子が含まれている場合、内部バッファーの大きさ (バイト単位) は、これらの指定子の積となります。OPEN 文にこれらの指定子がない場合、デフォルトの内部バッファーサイズは 8192 バイトになります。この内部バッファーは、最も大きなレコードを収容できるように拡張はされますが、縮小されることはありません。

Fortran ランタイムシステムのデフォルトでは、バッファリングを使用しないディスク書き出しを行います。つまり、デフォルトでは、バッファーにためて、後でディスクに書き出す代わりに、各レコードが書かれるとすぐにディスクに書き出されます。

バッファリング付きの書き出しを有効にするには (つまり、バッファーがディスクに書き出されるまでディスクデバイスが内部バッファーにためられようにするには)、次のいずれかを使用します。

OPEN 文の BUFFERED 指定子は、-assume buffered_io (Linux) または /assume:buffered_io (Windows*) オプションよりも優先されます。どちらも設定されなかった場合 (デフォルト)、FORT_BUFFERED 環境変数が実行時に使用されます。

OPEN 文の BUFFERED 指定子は、特定の論理ユニットに適用されます。一方、-assume nobuffered_io (Linux) または /assume:nobuffered_io (Windows*) オプションと FORT_BUFFERED 環境変数はすべての Fortran ユニットに適用されます。

通常、バッファリング付きの書き込みを使用すると、より大きなデータブロックがディスクに書き込まれ、書き込み回数が減るため、ディスク I/O の効率が向上します。しかし、バッファリングされた書き出しの使用時にシステム障害が起こると、その時点でディスクに書き出されていなかったレコードが失われます

入出力性能を向上させる実験を行うときには、BUFFERED 指定子を切り替えてください。BUFFERCOUNT 値を増やしたり、BLOCKSIZE 値を増やしたり減らしたりして、1 回のディスク入出力で読み取られるデータの量を変更できます。

ネットワークを介して入出力アクセスを行う場合、ネットワーク上で送られるネットワーク・データのブロックサイズがアプリケーションの効率に影響することに注意してください。ネットワーク・データを読み取る際、BUFFERCOUNT を増やすという、効率的なディスクの読み取りと同じアドバイスに従ってください。ネットワーク経由でデータを書き込む際、いくつかの点を考慮する必要があります。

WRITE 文の完了時に、たとえバッファリングしなくても、データがディスクに書き出されたという保証はありません: オペレーティング・システムでは、実際のディスクへの書き出しが遅延します。FLUSH ルーチンを呼び出して、データをディスクに書き出します。このルーチンを使用すると、アプリケーションの処理速度を大幅に低下するため、確実に必要な場合のみに FLUSH を使用してください。

Linux* のみ: レコードを書き出す際、入出力レコードは UBC (unified buffer cache) システムバッファーに書き出されることに注意してください。入出力レコードをプログラムバッファーから UBC システムバッファーへ書き出すようにするには、フラッシュ・ライブラリー・ルーチンを使用します。フラッシュを呼び出すと、ユーザーバッファーの先読みデータを破棄します。詳細は、「Language Reference」(英語) の「FLUSH」を参照してください。

RECL を指定する

レコード長 (OPEN 文の RECL 指定子) とそのオーバーヘッドの和は、ブロックサイズの倍数または除数で、デバイスによって異なります。例えば、BLOCKSIZE が 8192 だった場合、RECL としては 24576 (3 倍) や 1024 (8 で割った値) が考えられます。

RECL 値は、ブロックの容量にできるだけ近い値が設定されているべきです (ただし容量を超えてはなりません)。このような値であれば、各操作で可能な限り多くのデータを扱え、ブロック内の無駄な空間が最小限に抑えられるので、転送の効率が高くなります。ブロックの容量を超える値を使用すると、超えた分のデータだけで 1 ブロックを消費することになるので、移動の効率が大幅に低下します。これは、バッファーのために余分なメモリーを割り当て、部分的なブロックを書き出すという非効率的な操作が行われることが原因です。

書式付きファイルの RECL 値の単位は常に 1 バイト単位です。書式なしファイルの場合、ASSUME 指定子の -assume byterecl (Linux) または /assume:byterecl (Windows*) オプションで 1 バイト単位を指定しない限り、RECL には 4 バイト単位が使用されます。

最適なレコード形式を使用する

移植性の理由から、特定のレコード形式が必要な場合、次に示すように最も効率的な形式を選択してください。

リダイレクトされた標準入力ファイルから読み取る

Fortran ランタイムシステムは標準入力の整合性を保つ処理を行うため、標準入力がファイルからリダイレクトされると、読み取りが非常に遅くなります。例えば、myprogram.exe < myinput.data> のようなコマンドを使用した場合、データは READ(*) または READ(5) 文で読み取られ、パフォーマンスは悪くなります。この問題を回避するためには、次のいずれかを行います。

このような手法を活用するには、プログラムが標準入力ファイルの共有を使用しないように注意してください。

インテル® Fortran データファイルと入出力に関する詳細は、「アプリケーションのビルド」の「ファイル、デバイス、入出力」を参照してください。OPEN 文の指定子とデフォルトについては「Language Reference」(英語) の「Open」を参照してください。


このヘルプトピックについてのフィードバックを送信

© 1996-2010 Intel Corporation. 無断での引用、転載を禁じます。