Previous | Contents | Index |
The following sections discuss these: topics:
On Alpha systems, many of the array access efficiency techniques described in this section are applied automatically by the DIGITAL Fortran 90 loop transformation optimizations (see Section 5.8.1) or by the KAP for DIGITAL Fortran 90 for DIGITAL UNIX Systems performance preprocessor (described in Section 5.1.1).
Several aspects of array use can improve run-time performance:
A = A + 1. |
REAL :: A(100,100) A = 0.0 A = A + 1. ! Increment all elements of A by 1 . . . WRITE (8) A ! Fast whole array use |
TYPE X INTEGER A(5) END TYPE X . . . TYPE (X) Z WRITE (8) Z%A ! Fast array structure component use |
INTEGER X(3,5), Y(3,5), I, J Y = 0 DO I=1,3 ! I outer loop varies slowest DO J=1,5 ! J inner loop varies fastest X (I,J) = Y(I,J) + 1 ! Inefficient row-major storage order END DO ! (rightmost subscript varies fastest) END DO . . . END PROGRAM |
INTEGER X(3,5), Y(3,5), I, J Y = 0 DO J=1,5 ! J outer loop varies slowest DO I=1,3 ! I inner loop varies fastest X (I,J) = Y(I,J) + 1 ! Efficient column-major storage order END DO ! (leftmost subscript varies fastest) END DO . . . END PROGRAM |
INTEGER X(5,3), Y(5,3), I, J Y = 0 DO I=1,3 ! I outer loop varies slowest DO J=1,5 ! J inner loop varies fastest X (J,I) = Y(J,I) + 1 ! Efficient column-major storage order END DO ! (leftmost subscript varies fastest) END DO . . . END PROGRAM |
REAL A (512,100) DO I = 2,511 DO J = 2,99 A(I,J)=(A(I+1,J-1) + A(I-1, J+1)) * 0.5 END DO END DO |
For More Information:
On arrays and their data declaration statements, see the
DIGITAL Fortran Language Reference Manual.
In Fortran 90, there are two general types of array arguments:
When passing arrays as arguments, either the starting (base) address of the array or the address of an array descriptor is passed:
Passing an assumed-shape array or array pointer to an explicit-shape array can slow run-time performance. This is because the compiler needs to create an array temporary for the entire array. The array temporary is created because the passed array may not be contiguous and the receiving (explicit-shape) array requires a contiguous array. When an array temporary is created, the size of the passed array determines whether the impact on slowing run-time performance is slight or severe.
Table 5-3 summarizes what happens with the various combinations of array types. The amount of run-time performance inefficiency depends on the size of the array.
Input Arguments Array Types | Explicit-Shape Arrays | Deferred-Shape and Assumed-Shape Arrays |
---|---|---|
Explicit-Shape Arrays | Very efficient. Does not use an array temporary. Does not pass an array descriptor. Interface block optional. | Efficient. Only allowed for assumed-shape arrays (not deferred-shape arrays). Does not use an array temporary. Passes an array descriptor. Requires an interface block. |
Deferred-Shape and Assumed-Shape Arrays |
When passing an allocatable array, very efficient. Does not use an
array temporary. Does not pass an array descriptor. Interface block
optional.
When not passing an allocatable array, not efficient. Instead use allocatable arrays whenever possible. Uses an array temporary. Does not pass an array descriptor. Interface block optional. |
Efficient. Requires an assumed-shape or array pointer as dummy argument. Does not use an array temporary. Passes an array descriptor. Requires an interface block. |
Improving overall I/O performance can minimize both device I/O and actual CPU time. The techniques listed in this section can greatly improve performance in many applications.
A bottleneck limits the maximum speed of execution by being the slowest process in an executing program. In some programs, I/O is the bottleneck that prevents an improvement in run-time performance. The key to relieving I/O bottlenecks is to reduce the actual amount of CPU and I/O device time involved in I/O. Bottlenecks may be caused by one or more of the following:
Improved coding practices can minimize actual device I/O, as well as the actual CPU time.
DIGITAL offers software solutions to system-wide problems like
minimizing device I/O delays (see Section 5.1.1).
5.5.1 Use Unformatted Files Instead of Formatted Files
Use unformatted files whenever possible. Unformatted I/O of numeric data is more efficient and more precise than formatted I/O. Native unformatted data does not need to be modified when transferred and will take up less space on an external file.
Conversely, when writing data to formatted files, formatted data must be converted to character strings for output, less data can transfer in a single operation, and formatted data may lose precision if read back into binary form.
To write the array A(25,25) in the following statements, S1 is more efficient than S2:
S1 WRITE (7) A S2 WRITE (7,100) A 100 FORMAT (25(' ',25F5.21)) |
Although formatted data files are more easily ported to other systems,
DIGITAL Fortran 90 can convert unformatted data in several formats (see
Chapter 10).
5.5.2 Write Whole Arrays or Strings
The general guidelines about array use discussed in Section 5.4 also apply to reading or writing an array with an I/O statement.
To eliminate unnecessary overhead, write whole arrays or strings at one
time rather than individual elements at multiple times. Each item in an
I/O list generates its own calling sequence. This processing overhead
becomes most significant in implied-DO loops. When accessing whole
arrays, use the array name (Fortran 90 array syntax) instead of using
implied-DO loops.
5.5.3 Write Array Data in the Natural Storage Order
Use the natural ascending storage order whenever possible. This is column-major order, with the leftmost subscript varying fastest and striding by 1 (see Section 5.4). If a program must read or write data in any other order, efficient block moves are inhibited.
If the whole array is not being written, natural storage order is the best order possible.
If you must use an unnatural storage order, in certain cases
it might be more efficient to transfer the data to memory and reorder
the data before performing the I/O operation.
5.5.4 Use Memory for Intermediate Results
Performance can improve by storing intermediate results in memory rather than storing them in a file on a peripheral device. One situation that may not benefit from using intermediate storage is when there is a disproportionately large amount of data in relation to physical memory on your system. Excessive page faults can dramatically impede virtual memory performance.
If you are primarily concerned with the CPU performance of the system,
consider using a memory file system (mfs) virtual disk to hold any
files your code reads or writes (see mfs(1)).
5.5.5 Enable Implied-DO Loop Collapsing
DO loop collapsing reduces a major overhead in I/O processing. Normally, each element in an I/O list generates a separate call to the DIGITAL Fortran 90 RTL. The processing overhead of these calls can be most significant in implied-DO loops.
DIGITAL Fortran 90 reduces the number of calls in implied-DO loops by replacing up to seven nested implied-DO loops with a single call to an optimized run-time library I/O routine. The routine can transmit many I/O elements at once.
Loop collapsing can occur in formatted and unformatted I/O, but only if certain conditions are met:
For More Information:
Variable format expressions (a DIGITAL Fortran extension) are almost as flexible as run-time formatting, but they are more efficient because the compiler can eliminate run-time parsing of the I/O format. Only a small amount of processing and the actual data transfer are required during run time.
On the other hand, run-time formatting can impair performance significantly. For example, in the following statements, S1 is more efficient than S2 because the formatting is done once at compile time, not at run time:
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) |
Records being read or written are transferred between the user's program buffers and one or more disk block I/O buffers, which are established when the file is opened by the DIGITAL Fortran 90 RTL. Unless very large records are being read or written, multiple logical records can reside in the disk block I/O buffer when it is written to disk or read from disk, minimizing physical disk I/O.
You can specify the size of the disk block I/O buffer by using the OPEN statement BLOCKSIZE specifier; the default size can be obtained from fstat(2). If you omit the BLOCKSIZE specifier in the OPEN statement, it is set for optimal I/O use with the type of device the file resides on.
The default for BUFFERCOUNT is 1. Any experiments to improve I/O performance should increase the BUFFERCOUNT value and not the BLOCKSIZE value, to increase the amount of data read by each disk I/O.
When writing records, be aware that I/O records are written to unified buffer cache (UBC) system buffers. To request that I/O records be written from program buffers to the UBC system buffers, use the flush library routine (see flush(3f) and Chapter 12). Be aware that calling flush also discards read-ahead data in user buffer.
To request that UBC system buffers be written to disk, use the fsync library routine (see fsync(3f) and Chapter 12).
When UBC buffers are written to disk depends on UBC characteristics on
the system, such as the vm-ubcbuffers attribute (see the DIGITAL UNIX System Tuning and Performance
guide).
5.5.8 Specify RECL
The sum of the record length (RECL specifier in an OPEN statement) and its overhead is a multiple or divisor of the blocksize, which is device specific. For example, if the BLOCKSIZE is 8192 then RECL might be 24576 (a multiple of 3) or 1024 (a divisor of 8).
The RECL value should fill blocks as close to capacity as possible (but not over capacity). Such values allow efficient moves, with each operation moving as much data as possible; the least amount of space in the block is wasted. Avoid using values larger than the block capacity, because they create very inefficient moves for the excess data only slightly filling a block (allocating extra memory for the buffer and writing partial blocks are inefficient).
The RECL value unit for formatted files is always 1-byte units. For unformatted files, the RECL unit is 4-byte units, unless you specify the -assume byterecl option to request 1-byte units (see Section 3.5).
When porting unformatted data files from non-DIGITAL systems, see
Section 10.4.5.
5.5.9 Use the Optimal Record Type
Unless a certain record type is needed for portability reasons (see Section 7.4.3), choose the most efficient type, as follows:
For More Information:
Other source coding guidelines can be implemented to improve run-time performance.
The amount of improvement in run-time performance is related to the
number of times a statement is executed. For example, improving an
arithmetic expression executed within a loop many times has the
potential to improve performance, more than improving a similar
expression executed once outside a loop.
5.6.1 Avoid Small Integer and Small Logical Data Items
Avoid using integer or logical data less than 32 bits, because the smallest unit of efficient access on Alpha systems is 32 bits.
Accessing a 16-bit (or 8-bit) data type can result in a sequence of machine instructions to access the data, rather than a single, efficient machine instruction for a 32-bit data item.
To minimize data storage and memory cache misses with arrays, use
32-bit data rather than 64-bit data, unless you require the greater
numeric range of 8-byte integers or the greater range and precision of
double precision floating-point numbers.
5.6.2 Avoid Mixed Data Type Arithmetic Expressions
Avoid mixing integer and floating-point (REAL) data in the same computation. Expressing all numbers in a floating-point arithmetic expression (assignment statement) as floating-point values eliminates the need to convert data between fixed and floating-point formats. Expressing all numbers in an integer arithmetic expression as integer values also achieves this. This improves run-time performance.
For example, assuming that I and J are both INTEGER variables, expressing a constant number (2.) as an integer value (2) eliminates the need to convert the data:
Original Code: |
INTEGER I, J
I = J / 2. |
Efficient Code: |
INTEGER I, J
I = J / 2 |
For applications with numerous floating-point operations, consider using the -fp_reorder option (see Section 5.8.9) if a small difference in the result is acceptable.
You can use different sizes of the same general data type in an expression with minimal or no effect on run-time performance. For example, using REAL, DOUBLE PRECISION, and COMPLEX floating-point numbers in the same floating-point arithmetic expression has minimal or no effect on run-time performance.
Previous | Next | Contents | Index |