You may need to use asynchronous I/O for speed and efficiency in scientific programs that perform I/O for large amounts of data. Synchronous I/O blocks the execution of an application until the I/O operation completes. Asynchronous I/O allows an application to continue processing while the I/O operation is performed in the background. You can modify applications to take advantage of the ability to overlap processing and I/O operations. Multiple asynchronous I/O operations can also be performed simultaneously on multiple files that reside on independent devices. For a complete description of the syntax and language elements that you require to use this feature, see the XL Fortran for AIX Language Reference under the topics:
The effect of executing an asynchronous data transfer operation will be as if the following steps were performed in the order specified, with steps (6)-(9) possibly occurring asynchronously:
You can use Fortran asynchronous READ and WRITE statements to initiate asynchronous data transfers in Fortran. Execution continues after the asynchronous I/O statement, whether or not the actual data transfer has completed.
A program may synchronize itself with a previously initiated asynchronous I/O statement by using a WAIT statement. There are two forms of the WAIT statement:
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) ....The variable you specified in the DONE= specifier is set to "true" if the corresponding asynchronous I/O statement completes. Otherwise, it is set to "false".
The actual data transfer can take place in the following cases:
Because of the nature of asynchronous I/O, the actual completion time of the request cannot be predicted.
You specify Fortran asynchronous READ and WRITE statements by using the ID= specifier. The value set for the ID= specifier by an asynchronous READ or WRITE statement must be the same value specified in the ID= specifier in the corresponding WAIT statement. You must preserve this value until the associated asynchronous I/O statement has completed.
The following program shows a valid asynchronous WRITE statement:
program sample0 integer, dimension(1000):: a integer idvar a = (/(i,i=1,1000)/) WRITE(10,ID=idvar) a WAIT(ID=idvar) end
The following program is not valid because XL Fortran destroys the value of the asynchronous I/O identifier before the associated WAIT statement:
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
An application that uses asynchronous I/O typically improves performance by overlapping processing with I/O operations. The following is a simple example:
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
To maximize the benefits of asynchronous I/O, you should only use it for large contiguous data items.
It is possible to perform asynchronous I/O on a large number of small items, but the overall performance will suffer. This is because extra processing overhead is required to maintain each item for asynchronous I/O. Performing asynchronous I/O on a larger number of small items is strongly discouraged. The following are two examples:
Performing asynchronous I/O on unformatted sequential files is less efficient. This is because each record might have a different length, and these lengths are stored with the records themselves. You should use unformatted direct access, if possible, to maximize the benefits of asynchronous I/O.
There are situations when the compiler must generate a temporary variable to hold the result of an I/O item expression. In such cases, synchronous I/O is performed on the temporary variable regardless of the mode of transfer that you specified in the I/O statement. The following are examples of such cases:
integer a(5), b(3) b = (/1,3,5/) read(99, id=i) a(b)
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
Before a Fortran application that is using asynchronous I/O can run on an AIX system, you must enable AIX asynchronous I/O. If you did not enable AIX asynchronous I/O, a Fortran program using asynchronous I/O statements cannot be loaded. This will result in the following messages being displayed:
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
For information on how to configure your system for asynchronous I/O see "Changing Attributes for Asynchronous I/O" in AIX Version 4 Kernel Extensions and Device Support Programming Concepts. If a Fortran program is not using Fortran asynchronous I/O statements, it will run regardless of the availability of AIX asynchronous I/O.
If there are no asynchronous I/O statements in an application, there is no change in the way you build an application. For example, for dynamic linking, you specify:
xlf95 -o t t.f
For static linking, you specify:
xlf95 -o t t.f -bnso -bnodelcsect -bI:/lib/syscalls.exp
If there are asynchronous I/O statements in an application, you need additional command-line options for static linking. For example:
xlf95 -o t t.f -lc -bnso -bnodelcsect \ -bI:/lib/syscalls.exp -bI:/lib/aio.exp
Note that the additional options are -lc and -bI:/lib/aio.exp.
The following table summarizes the options that you need to bind applications in different situations:
Figure 25. Table for Binding an Application Written Only in Fortran
Fortran program using asynchronous I/O statements | ||
---|---|---|
Type of Linking | Yes | No |
Dynamic | xlf95 -o t t.f | xlf95 -o t t.f |
Static |
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 |
Fortran program using asynchronous I/O statements | ||
---|---|---|
Type of Linking | Yes | No |
Dynamic | xlf95 -o t t.f c.o -lc | xlf95 -o t t.f c.o -lc |
Static |
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 |
Note: c.o is an object file of routines written in C. |
You can bind an application that uses asynchronous I/O on a system with AIX asynchronous I/O disabled. However, you must run the resulting executable on a system with AIX asynchronous I/O enabled.
For an asynchronous data transfer, errors or end-of-file conditions might occur either during execution of the data transfer statement or during subsequent data transfer. If these conditions do not result in the termination of the program, you can detect these conditions via ERR=, END= and IOSTAT= specifiers in the data transfer or in the matching WAIT statement.
Execution of the program terminates if an error condition occurs during execution or during subsequent data transfer of an input/output statement that contains neither an IOSTAT= nor an ERR= specifier. In the case of a recoverable error, if the IOSTAT= and ERR= specifiers are not present, the program terminates if you set the err_recovery run-time option to no. If you set the err_recovery run-time option to yes, recovery action occurs, and the program continues.
If an asynchronous data transfer statement causes either of the following events, a matching WAIT statement cannot run because the ID= value is not defined:
The XL Fortran thread-safe I/O library libxlf90_r.a provides support for parallel execution of Fortran I/O statements. For Fortran programs that contain I/O statements in a parallelized loop, or that create multiple threads and execute I/O statements from different threads at the same time, you must use this library. In other words, to perform Fortran I/O in parallel, you must link applications with this library to get expected results.
However, note that on AIX Version 4.3, a link is provided from the libxlf90_r.a library to the libxlf90.a library. You do not need to link with separate libraries depending on whether you are creating a threaded or a non-threaded application. XL Fortran determines at run time whether your application is threaded.
During parallel execution, multiple threads might perform I/O operations on the same file at the same time. If they are not synchronized, the results of these I/O operations could be shuffled or merged or both, and the application might produce incorrect results or even terminate. The XL Fortran thread-safe I/O library synchronizes I/O operations for parallel applications. It performs the synchronization within the I/O library and it is transparent to application programs. The purpose of the synchronization is to ensure the integrity and correctness of each individual I/O operation. However, the thread-safe I/O library does not have control over the order in which threads execute I/O statements. Therefore, the order of records read in or written out is not predictable under parallel I/O operations. Refer to Parallel I/O Issues for details.
For external files, the synchronization is performed on a per-unit basis. The XL Fortran thread-safe I/O library ensures that only one thread can access a particular logical unit to prevent several threads from interfering with each other. When a thread is performing an I/O operation on a unit, other threads attempting to perform I/O operations on the same unit must wait until the first thread finishes its operation. Therefore, the execution of I/O statements by multiple threads on the same unit is serialized. However, the thread-safe I/O library does not prevent threads from operating on different logical units in parallel. In other words, parallel access to different logical units is not necessarily serialized.
The XL Fortran thread-safe I/O library sets its internal locks to synchronize access to logical units. This should not have any functional impact on the I/O operations performed by a Fortran program. Also, it will not impose any additional restrictions to the operability of Fortran I/O statements, except for the use of I/O statements in a signal handler that is invoked asynchronously. Refer to Use of I/O Statements in Signal Handlers for details.
The Fortran standard prohibits a function reference from appearing in an expression anywhere in an I/O statement if such a reference causes another I/O statement to run. This restriction still applies with the XL Fortran thread-safe I/O library.
The order in which parallel threads perform I/O operations is not predictable. The XL Fortran thread-safe I/O library does not have control over the ordering. It will allow whichever thread that executes an I/O statement on a particular logical unit and obtains the lock on it first to proceed with the operation. Therefore, only use parallel I/O in cases where at least one of the following is true:
In these cases, results of the I/O operations are independent of the order in which threads execute. However, you might not get the performance improvements that you expect since the I/O library serializes parallel access to the same logical unit from multiple threads. Examples of these cases are as follows:
do i = 1, 10 write(4, '(i4)', rec = i) a(i) enddo
do i = 1, 10 read(4) a(i) enddo sort array a
do i = 11, 20 write(i, '(i4)') a(i - 10) enddo
For multiple threads to write to or read from the same sequential-access file, the order of records written out or read in depends on the order in which the threads execute the I/O statement on them. This order, as stated previously, is not predictable. Therefore, the result of an application could be incorrect if it assumes records are sequentially related and cannot be arbitrarily written out or read in. For example, if the following loop is parallelized, the numbers printed out will no longer be in the sequential order from 1 to 500 as the result of a serial execution:
do i = 1, 500 print *, i enddo
Applications that depend on numbers being strictly in the specified order will not work correctly.
The XL Fortran run-time option multconn=yes allows connection of the same file to more than one logical unit simultaneously. Since such connections can only be made for reading (ACCESS='READ'), access from multiple threads to logical units that are connected to the same file will produce predictable results.
There are basically two kinds of signals in the AIX (POSIX) signal model: synchronously and asynchronously generated signals. Signals caused by the execution of some code of a thread, such as a reference to an unmapped, protected, or bad memory (SIGSEGV or SIGBUS), floating-point exception (SIGFPE), execution of a trap instruction (SIGTRAP), and execution of illegal instructions (SIGILL), are said to be synchronously generated. Signals may also be generated by events outside the process, for example, SIGINT, SIGHUP, SIGQUIT, SIGIO, and so on. Such events are referred to as interrupts. Signals that are generated by interrupts are said to be asynchronously generated.
The XL Fortran thread-safe I/O library is asynchronous signal unsafe. This means that the XL Fortran I/O statements cannot be used in a signal handler that is entered because of an asynchronously generated signal. The behavior of the system is undefined when an XL Fortran I/O statement is called from a signal handler that interrupts an I/O statement. However, it is safe to use I/O statements in signal handlers for synchronous signals.
Sometimes an application can guarantee that a signal handler is not entered asynchronously. For example, an application might mask signals except when it runs certain known sections of code. In such situations, the signal will not interrupt any I/O statements and other asynchronous signal unsafe functions. Therefore, you can still use Fortran I/O statements in an asynchronous signal handler.
A much easier and safer way to handle asynchronous signals is to block signals in all threads and to explicitly wait (using sigwait()) for them in one or more separate threads. The advantage of this approach is that the handler thread can use Fortran I/O statements as well as other asynchronous signal unsafe routines.
When a thread enables asynchronous thread cancellability, any cancellation request is acted upon immediately. The XL Fortran thread-safe I/O library is not asynchronous thread cancellation safe. The behavior of the system is undefined if a thread is cancelled asynchronously while it is in the XL Fortran thread-safe I/O library.