大量に上るデータの I/O を実行する科学プログラムでは、速度と効率を高めるために非同期 I/O が必要とされる場合があります。同期 I/O では、I/O 操作が完了するまでアプリケーションの実行がブロックされます。非同期 I/O では、I/O 操作をバックグラウンドで実行しながら、アプリケーションによる処理を続行できます。処理と I/O 操作を並行して行う能力を使用する場合にはアプリケーションを修正できます。独立した装置に常駐する複数のファイルに対して、複数の非同期 I/O 操作を同時に実行することもできます。この機能を使用するために必要な構文および言語エレメントに関する詳細な説明については、「XL Fortran for AIX ランゲージ・リファレンス 」で以下のトピックを参照してください。
非同期データ転送操作を実行すると、以下のステップを指定順序で実行した場合と同様の効果が得られ、ステップ (6) から (9) が (可能であれば) 非同期で発生します。
Fortran で非同期データ転送を開始するには、Fortran の非同期 READ および WRITE ステートメントを使用します。実際のデータ転送が完了したかどうかに関係なく、非同期 I/O ステートメント後も実行が継続されます。
WAIT ステートメントを使えば、先に開始された非同期 I/O ステートメントとプログラムを同期化することができます。 WAIT ステートメントには 2 つの形式があります。
integer idvar integer, dimension(1000):: a .... READ(unit_number,ID=idvar) a .... WAIT(ID=idvar) ....
integer idvar logical done integer, dimension(1000):: a .... READ(unit_number,ID=idvar) a .... WAIT(ID=idvar, DONE=done) ....
DONE= 指定子で指定した変数は、対応する非同期 I/O ステートメントが完了している場合には、真に設定されます。それ以外の場合は、偽に設定されます。
実際のデータ転送は、以下の場合に起こると思われます。
非同期 I/O の性質上、実際に要求が完全に果たされる時間は予測できません。
Fortran 非同期 READ および WRITE ステートメントは、 ID= 指定子を使って指定します。非同期 READ または WRITE ステートメントによる ID= 指定子の値セットは、対応する WAIT ステートメント中の ID= 指定子と同じでなければなりません。関連した非同期 I/O ステートメントが完了するまでこの値を保持しておく責任はプログラマーにあります。
以下のプログラムでは、有効な非同期 WRITE ステートメントを示します。
program sample0 integer, dimension(1000):: a integer idvar a = (/(i,i=1,1000)/) WRITE(10,ID=idvar) a WAIT(ID=idvar) end
XL Fortran では、関連する WAIT ステートメントの前の非同期 I/O 識別子の値が壊されるため、以下のプログラムは無効です。
program sample1 integer, dimension(1000):: a integer idvar a = (/(i,i=1,1000)/) WRITE(10,ID=idvar) a idvar = 999 ! Valid id is destroyed. WAIT(ID=idvar) end
非同期 I/O を使用するアプリケーションは一般に、I/O 操作を並行処理してパフォーマンスを向上させます。以下に簡単な例を示します。
program sample2 integer (kind=4), parameter :: isize=1000000, icol=5 integer (kind=4) :: i, j, k integer (kind=4), dimension(icol) :: handle integer (kind=4), dimension(isize,icol), static :: a, a1 ! ! Opens the file for both synchronous and asynchronous I/O. ! open(20,form="unformatted",access="direct", & status="scratch", recl=isize*4,asynch="yes") ! ! This loop overlaps the initialization of a(:,j) with ! asynchronous write statements. ! ! NOTE: The array is written out one column at a time. ! Since the arrays in Fortran are arranged in column ! major order, each WRITE statement writes out a ! contiguous block of the array. ! do 200 j = 1, icol a(:,j) = (/ (i*j,i=1,isize) /) write(20, id=handle(j), rec=j) a(:,j) 200 end do ! ! Wait for all writes to complete before reading. ! do 300 j = 1, icol wait(id=handle(j)) 300 end do ! ! Reads in the first record. ! read(20, id=handle(1), rec=1) a1(:,1) do 400 j = 2, icol k = j - 1 ! ! Waits for a previously initiated read to complete. ! wait(id=handle(k)) ! ! Initiates the next read immediately. ! read(20, id=handle(j), rec=j) a1(:,j) ! ! While the next read is going on, we do some processing here. ! do 350 i = 1, isize if (a(i,k) .ne. a1(i,k)) then print *, "(",i,",",k,") & & expected ", a(i,k), " got ", a1(i,k) end if 350 end do 400 end do ! ! Finish the last record. ! wait(id=handle(icol)) do 450 i = 1, isize if (a(i,icol) .ne. a1(i,icol)) then print *, "(",i,",",icol,") & & expected ", a(i,icol), " got ", a1(i,icol) end if 450 end do close(20) end
非同期 I/O の利点を最大に生かすには、非同期 I/O を大量の連続データ項目に対して実行することをお勧めします。
大量の小さな項目に対して非同期 I/O を実行することも可能ですが、パフォーマンス自体は悪化します。これは、非同期 I/O で各項目を保守するために余分の処理オーバーヘッドが必要とされるという状況に起因します。非同期 I/O を大量の小さな項目に対して実行することは控えたほうがよいでしょう。以下に例をいくつか示します。
不定様式順次ファイルに非同期 I/O を実行する場合、各レコードには異なる長さがあり、それらの長さはレコードそのものと一緒に格納されるので、効率は劣るものになります。非同期 I/O の利点を最大に引き出すには、できれば不定様式直接アクセス|または不定様式ストリーム・アクセスを使用することをお勧めします。
状況によっては、コンパイラーで一時変数を生成して、I/O 項目式の結果を保持する必要がある場合もあります。そのような場合、 I/O ステートメントでどのようなモードが指定されていても、一時変数に対しては非同期 I/O が実行されます。以下に、その事例を示します。
integer a(5), b(3) b = (/1,3,5/) read(99, id=i) a(b)
real a(10) read(99,id=i) a((/1,3,5/))
write(99,id=i) 1000
integer a parameter(a=1000) write(99,id=i) a
type mytype integer a integer b end type mytype write(99,id=i) mytype(4,5)
write(99,id=i) 99+100
write(99,id=i) a+b
external ff real(8) ff write(99,id=i) ff()
write(99,id=i) (/1,2,3,4,5/)
integer a(5),b(5) write(99,id=i) a+b
非同期 I/O を使用する Fortran アプリケーションを AIX システムで実行するには、AIX 非同期 I/O を使用可能にする必要があります。 AIX 非同期 I/O が使用できなければ、非同期 I/O ステートメントを使用する Fortran プログラムをロードできません。その結果、次のようなメッセージが表示されます。
Could not load program asyncio Symbol kaio_rdwr in ksh is undefined Symbol listio in ksh is undefined Symbol acancel in ksh is undefined Symbol iosuspend in ksh is undefined Error was: Exec format error
実際のシステムを構成して非同期 I/O を行う方法については、「AIX Version 4 Kernel Extensions and Device Support Programming Concepts 」にある「Changing Attributes for Asynchronous I/O」を参照してください。 Fortran プログラムが Fortran 非同期 I/O ステートメントを使用しない場合、そのプログラムは AIX 非同期 I/O を使えるかどうかに関係なく実行されます。
アプリケーションに非同期 I/O ステートメントがない場合、アプリケーションの構築方法に変更はありません。たとえば、動的リンクでは、以下のようになります。
xlf95 -o t t.f
静的リンクでは、以下のようになります。
xlf95 -o t t.f -bnso -bnodelcsect -bI:/lib/syscalls.exp
アプリケーションに非同期 I/O ステートメントがある場合、静的リンクでは追加のコマンド行オプションが必要です。たとえば、次のようになります。
xlf95 -o t t.f -lc -bnso -bnodelcsect \ -bI:/lib/syscalls.exp -bI:/lib/aio.exp
追加オプションは -lc および -bI:/lib/aio.exp であることに注意してください。
以下の表では、異なる状況でアプリケーションをバインドするために必要なオプションを要約します。
表 22. Fortran だけで作成されたアプリケーションをバインドするための表
非同期 I/O ステートメントを使用する Fortran プログラム | ||
---|---|---|
リンクのタイプ | Yes | No |
動的 | xlf95 -o t t.f | xlf95 -o t t.f |
静的 |
xlf95 -o t t.f -bnso -bnodelcsect -bI:/lib/syscalls.exp -lc -bI:/lib/aio.exp |
xlf95 -o t t.f -bnso -bnodelcsect -bI:/lib/syscalls.exp |
表 23. Fortran と C の両方で作成されたアプリケーションをバインドするための 表 (C ルーチンは libc 非同期 I/O ルーチンを呼び出す)
非同期 I/O ステートメントを使用する Fortran プログラム | ||
---|---|---|
リンクのタイプ | Yes | No |
動的 | xlf95 -o t t.f c.o -lc | xlf95 -o t t.f c.o -lc |
静的 |
xlf95 -o t t.f c.o -bnso -bnodelcsect -bI:/lib/syscalls.exp -lc -bI:/lib/aio.exp |
xlf95 -o t t.f c.o -bnso -bnodelcsect -bI:/lib/syscalls.exp -lc -bI:/lib/aio.exp |
注 : c.o は C で作成されたルーチンのオブジェクト・ファイルです。 |
AIX 非同期 I/O を使用禁止にしたシステムで非同期 I/O を使用するアプリケーションをバインドすることは可能です。ただし、結果と使用して生じる実行可能モジュールは、 AIX 非同期 I/O が使用可能なシステム上で実行する必要があります。
非同期データ転送では、データ転送ステートメントの実行中または後続のデータ転送中にエラーまたはファイルの終わり条件が発生する場合があります。プログラムの終了時にこれらの条件が発生しない場合、プログラマーは、データ転送または WAIT ステートメントの突き合わせで ERR=、 END=、および IOSTAT= 指定子を使ってそれらの条件を検出できます。
IOSTAT= 指定子も ERR= 指定子も入っていない I/O ステートメントの実行中、またはその後続データ転送中にエラー条件が発生した場合、プログラムの実行が終了します。回復可能エラーの場合には、IOSTAT= および ERR= 指定子が存在しなければ、 err_recovery 実行時オプションが no に設定されている場合、プログラムは終了します。 err_recovery 実行時オプションが yes に設定されていれば、回復処置が発生し、プログラムは続行します。
非同期データ転送ステートメントが原因で以下の状況が発生する場合、 ID= 値が定義されていないので、WAIT ステートメントは許可されません。
XL Fortran スレッド・セーフ I/O ライブラリー libxlf90_r.a では、Fortran I/O ステートメントの並列実行がサポートされます。並列ループで I/O ステートメントが入っている、または異なるスレッドからマルチスレッドを作成すると同時に I/O ステートメントを実行するプログラムでは、このライブラリーを使用する必要があります。つまり、Fortran I/O を並列実行する場合、期待どおりの結果を得るには、アプリケーションをこのライブラリーとリンクしなければなりません。
ただし、AIX オペレーティング・システム・レベル バージョン 4.3 以降では、 libxlf90_r.a ライブラリーから libxlf90.a へとリンクされることに注意してください。スレッド化されたアプリケーションかスレッド化されていないアプリケーションのいずれを作成しているにしても、別々のライブラリーを使用してリンクする必要はありません。 XL Fortran は、実行時にアプリケーションがスレッド化されているかどうかを判別します。
並列実行中には、マルチスレッドで I/O 操作が同じファイル上で同時に実行される場合があります。操作が同期化されていなければ、結果は切り捨てられるか結合される、またはその両方が当てはまる場合があり、アプリケーションは間違った結果を作成し、破壊される場合さえあります。 XL Fortran スレッド・セーフ I/O ライブラリーは並列アプリケーションの I/O 操作を同期化します。同期化は I/O ライブラリー内で実行され、アプリケーション・プログラムに対して透過的です。同期化の目的は、個々の I/O 操作の整合性と正確さを保証することにあります。ただし、スレッド・セーフ I/O ライブラリーは、スレッドが I/O ステートメントを実行する順序を制御しません。したがって、並列 I/O 操作では、読み取ったり書き出したりするレコードの順序を予測することはできません。詳細については、 並列 I/O の問題を参照してください。
外部ファイルの場合、同期化は装置単位で実行されます。 XL Fortran スレッド・セーフ I/O ライブラリーでは必ず、特定の論理装置にアクセスするスレッドが 1 つだけになり、いくつかのスレッドが互いに干渉することがなくなります。スレッドがある装置上で I/O 操作を実行しているときに同じ装置上で I/O 操作を実行しようとする別のスレッドは、最初のスレッドが操作を終了するまで待機しなければなりません。したがって、同じ装置上のマルチスレッドによる I/O ステートメントの実行は逐次化されます。しかし、スレッド・セーフ I/O ライブラリーによって、スレッドが別の論理装置上で並列操作を行わなくなるということはありません。つまり、異なる論理装置への並列アクセスは実質的に逐次化されません。
XL Fortran スレッド・セーフ I/O ライブラリーは、論理装置へのアクセスを同期化するために内部ロックを設定します。これによって、Fortran プログラムで実行される I/O 操作に機能面での影響が及ぶことはありません。また、Fortran I/O ステートメントの操作性に付加的な制限が課されることもありません。ただし、非同期的に起動されるシグナル・ハンドラーで I/O ステートメントを使用する場合はこの限りではありません。詳細については、 シグナル・ハンドラーでの I/O ステートメントの使用を参照してください。
Fortran の規格では、関数参照によって別の I/O ステートメントが実行される場合には、I/O ステートメントのどこにも式の中で関数参照を入れることができません。この制限は引き続き XL Fortran スレッド・セーフ I/O ライブラリーに適用されます。
並列スレッドが I/O 操作を実行する順序は予測できません。 XL Fortran スレッド・セーフ I/O ライブラリーでは、順序付けが制御されず、どんなスレッドでも特定の論理装置上の I/O ステートメントを実行し、そのロックを取得するものであれば、まずそのスレッドが操作を行うことになります。したがって、並列 I/O は、少なくとも以下のいずれかが真の場合にのみ使用できます。
これらの事例では、I/O 操作の結果はスレッドが実行される順序に依存していません。ただし、マルチスレッドから同じ論理装置に並列アクセスを行うと、 I/O ライブラリーよる逐次化が行われるため、速度は思ったほど上がらない場合があります。これらの事例を、以下に例で示します。
do i = 1, 10 write(4, '(i4)', rec = i) a(i) enddo
| do i = 1, 9 | write(4, '(i4)', pos = 1 + 5 * (i - 1)) a(i) | ! We use 5 above because i4 takes 4 file storage | ! units + 1 file storage unit for the record marker. | enddo
real a(100) do i = 1, 10 read(4) a(i) enddo call qsort_(a)
do i = 11, 20 write(i, '(i4)') a(i - 10) enddo
同じ順次アクセス・ファイルでマルチスレッドによる書き込みまたは読み取りを実行する場合、|あるいは |POS= 指定子を使用せずに同じストリーム・アクセス・ファイルで書き込みまたは読み取りを実行する場合、レコードの書き込みまたは読み取りの順序は、スレッドがそのファイルで I/O ステートメントを実行する順序に依存しています。前に述べたとおり、この順序は予測できません。したがって、アプリケーションの結果で、レコードが定順位でつながっており、書き込みや読み取りを任意に行えないと判断される場合、その結果は誤ったものになる可能性があります。このループが並列化されると、数字が逐次実行の結果と同様に 1 から 500 までの順序で出力されることはなくなります。
do i = 1, 500 print *, i enddo
その順序で厳密に配列された数字に依存するアプリケーションは、正確に作動しなくなります。
XL Fortran の実行時オプション multconn=yes を指定すると、同じファイルを同時に複数の論理装置に接続できます。そのような接続は読み取り (ACCESS='READ') だけに設定できるので、同じファイルに接続された論理装置へのマルチスレッドによるアクセスの結果は予想可能になります。
|POSIX シグナル・モデルには基本的に 2 種類のシグナル、つまり、同期生成シグナル と非同期生成シグナル があります。非マップ式メモリー、保護メモリー、または不良メモリー (SIGSEGV または SIGBUS) への参照、浮動小数点例外 (SIGFPE)、トラップ命令の実行 (SIGTRAP)、または無許可命令の実行 (SIGILL) など、スレッドのコードを実行して生成されるシグナルを、同期生成されたシグナルと言います。シグナルはプロセス外のイベント、たとえば、SIGINT、SIGHUP、 SIGQUIT、SIGIO といったイベントによっても生成されます。そのようなイベントは割り込みと呼ばれます。割り込みで生成されるシグナルを、非同期生成されたシグナルといいます。
XL Fortran スレッド・セーフ I/O ライブラリーは、非同期シグナルに対してセーフではありません。つまり、非同期生成シグナルであるために入力されるシグナル・ハンドラーでは XL Fortran I/O ステートメントを使用できません。 I/O ステートメントに割り込むシグナル・ハンドラーから XL Fortran I/O ステートメントが呼び出される場合、システムの動作は未定義です。ただし、同期シグナルのシグナル・ハンドラーで I/O ステートメントを使用することは問題ありません。
アプリケーションでシグナル・ハンドラーの同期入力はないことが保証される場合もあります。たとえば、特定の部分の認知コードの実行を除いて、シグナルをマスクするアプリケーションがあります。そのような状況では、シグナルが I/O ステートメントや、非同期シグナルにセーフでない機能に割り込むことはないことが知られています。したがって、非同期シグナル・ハンドラーでは引き続き Fortran I/O ステートメントを使用できます。
非同期シグナルをさらに簡単かつ安全に処理する方法は、すべてのスレッドでシグナルをブロックし、それらのスレッドを 1 つ以上の別個のスレッドで明示的に待機 (sigwait() を使用) することです。この方法による利点は、handler スレッドが、Fortran I/O ステートメントに加えて、非同期シグナルにセーフではない他のルーチンを使用できることです。
スレッドで非同期スレッドの取り消しを使用可能にすると、取り消し要求がある場合にはその要求がただちに処理されます。 XL Fortran スレッド・セーフ I/O ライブラリーは、非同期スレッドの取り消しに対してセーフではありません。スレッドが XL Fortran スレッド・セーフ I/O ライブラリーにある間にそのスレッドが非同期に取り消される場合、システムの動作は未定義です。