Ausschnittsweise wird hier aus
Ways to Achieve Run-Time Efficiency [...] Declaring Integer and Logical Variables Efficiently One way to increase run-time efficiency is to declare integer and logical sizes equal to the word size of your machine. The most portable (standard) solution is to use INTEGER and the default will be determined by the machine's architecture. For example, on a 32-bit machine, the defaults are INTEGER*4 and LOGICAL*4. On a 16-bit machine, the defaults are INTEGER*2 and LOGICAL*2. Using Efficient Data Types When more than one data type is possible for a variable, choose the data type according to the following hierarchy (in descending order of efficiency): * Integer. * Real. * Double Precision (REAL*8). * REAL*16. However, note that it is ideal to have variables that are used together in expressions to be all the same type, as discussed in the following section. Avoiding the Use of Mixed-Mode Expressions Avoiding mixed-mode expressions also increases run-time efficiency. For example, the following assignment statement, where INT is an integer, requires converting INT to a real number and then converting the result back to an integer during execution: INT = 1.0 + INT A more efficient assignment statement is shown below: INT = 1 + INT Eliminating Slow Arithmetic Operators When possible, replace slower arithmetic operations with faster operations. The arithmetic operations are listed below from fastest to slowest: * Addition and subtraction (+ -). * Multiplication (*). * Division (/). * Exponentiation (**). In some cases, multiplication operations can replace exponentiation. Addition operations can replace multiplication. And multiplication operations can replace division. For example: I = J**2 can be written as I = J * J A = 2.0 * B can be written as A = B + B X = Y / 10 can be written as X = Y * 0.1 ________________________________________________________________________ NOTE The last example might cause an error if you are porting your program to another system because the internal representation of Y might vary between systems. ________________________________________________________________________ Using PARAMETER Statements The PARAMETER statement can be used to compute constant expressions at compile time rather than at run time. Because of this, it makes sense to replace frequently-used constant expressions with a constant equivalent of the expression, defined by a PARAMETER statement. For example, in Table 5-1 , the code in the first column frequently uses the constant expression (32.0/2)**2. This expression must be computed everywhere it occurs. The code in the second column replaces all occurrences of this expression with the named constant X defined in a PARAMETER statement. This code will run faster because the value of the named constant is computed once, at compile time. Table 5-1. Using PARAMETER Statements --------------------------------------------------------------------------------------------- | | | | Some Code | A Faster Version | | | | --------------------------------------------------------------------------------------------- | | | | A = A * (32.0/2)**2 * B | PARAMETER (X = (32.0/2)**2) | | B = B * (32.0/2)**2 + Z | : | | C = C * (32.0/2)**2 * 2.0 | A = A * X * B | | | B = B * X + Z | | | C = C * X * 2.0 | | | | --------------------------------------------------------------------------------------------- As an ANSI and portability extension, the following intrinsic functions can be used in the PARAMETER statement if they have constant arguments: ABS CONJG IAND IMAG LGE LLT MOD QPROD CHAR DIM ICHAR IOR LGT MAX NINT CMPLX DPROD IEOR ISHFT LLE MIN NOT If these functions appear with constant arguments, they are evaluated (called) at compilation time. Thus, the evaluation need not be done at run time. For example, a string with a non-printing character can be constructed and given a name in a PARAMETER statement, such as: CHARACTER*10 RINGER PARAMETER (RINGER = 'RING' // CHAR(7H) // ' HERE') Using Statement Functions Statement functions, as translated by the FORTRAN compiler, are a macro facility. During compilation, a call to a statement function is replaced by the body of the statement function, with each actual argument substituted for the corresponding dummy argument. Usually the generated code runs faster than a call to an equivalent external function. However, if the statement function is called many times, the code space may be larger than the external call. Reducing External References Reducing external references eliminates unnecessary function calls as shown in the following example: C = LOG(A) + LOG(B) CAN BE WRITTEN AS C = LOG(A * B) Also, the following statement may require a procedure call to an exponential function procedure: X =Y**2 Rewriting the statement as follows, avoids the procedure call: X = Y * Y If a function is called more than once with the same arguments, you can eliminate the additional procedure calls by assigning the result to a temporary variable. For example, the following statements A = MIN(X,Y) + 1.0 B = MIN(X,Y) + 4.0 can be rewritten as follows: MINVAL = MIN(X,Y) A = MINVAL + 1.0 B = MINVAL + 4.0 Note that the rewritten statements above are more efficient, but readability is reduced. Combining Do Loops You can increase run-time efficiency by combining adjacent DO loops that are executed the same number of times. For example, the following statements: DO 100 I = 1,20 A(I) = B(I) + C(I) 100 CONTINUE DO 200 J = 1,20 X(J) = Y(J) + Z(J) 200 CONTINUE can be replaced with the following: DO 100 I = 1,20 A(I) = B(I) + C(I) X(I) = Y(I) + Z(I) 100 CONTINUE Eliminating Short DO Loops Breaking short DO loops with constant limits into separate statements eliminates the overhead associated with the loop. Note, however that removing DO loops makes programs longer. It is also not practical if the number of loop iterations is large. The following example shows a sample DO loop statement followed by replacement statements: Example loop: DO 50 I = 1,3 C(I) = A(I) * B(I) 50 CONTINUE Replacement statements: C(1) = A(1) * B(1) C(2) = A(2) * B(2) C(3) = A(3) * B(3) Eliminating Common Operations in Loops Minimizing operations inside of a loop is another way to maximize run-time efficiency. If the result of an operation is the same throughout the loop, move the expression before or after the loops so the expression is only executed once. An example loop and its replacement are shown below: Example loop: SUM = 0.0 DO 100 I = 1,N SUM = SUM + VALUE * A(I) 100 CONTINUE Replacement statements: SUM = 0.0 DO 100 I = 1,N SUM = SUM + A(I) 100 CONTINUE SUM = VALUE * SUM Using Efficient IF Statements In block IF statements, order the conditions so that the most likely condition is tested first. For example, if the value of ARG is three in most cases, write a compound IF statement as follows: IF (ARG .EQ. 3) THEN : ELSE IF (ARG .EQ. 1) THEN : ELSE IF (ARG .EQ. 2) THEN : ELSE : When using the logical operators .AND. and .OR. in an IF condition, the code generated only checks enough conditions to determine the result of the entire logical expression. If several logical expressions are connected with .OR., checking discontinues as soon as an expression evaluates to .TRUE. When .AND. is used, checking discontinues as soon as a .FALSE. condition is found. Therefore, order the conditions so the fewest number of checks are made. For example, if it is more likely that variable A will equal zero than it is that B will be greater than 100, write the IF statement as follows: IF ((A .EQ. 0) .OR. (B .GT. 100)) rather than IF ((B .GT. 100) .OR. (A .EQ. 0)) Avoiding the Use of Formatted Input/Output When possible, use unformatted I/O. Formatted I/O requires costly conversions between binary and ASCII format. When using formatted I/O, put the format string in a separate FORMAT statement instead of using a variable. For example, use the statement WRITE (6,10) VAR 20 FORMAT (F10.2) instead of: CHARACTER*7 A DATA A/'(F10.2)'/ WRITE (6,A) VAR Format specifiers contained in variables are not parsed when a program is compiled. Instead, a format processing routine is called by the compiled program each time the format is used. Specifying the Array Name for I/O When reading or writing an array, specify the array name instead of using an implied DO loop. This allows the array to be operated on as a whole, instead of performing individual operations for each element. For example, specify WRITE (6, '(A1)') MYARRAY instead of WRITE (6, '(A1)') (MYARRAY(I,J), I=1,10), J=1,10) Avoiding the Use of Range Checking Turn range checking on only for development code. This option causes extra code to be included in your program to check the bounds when a substring or array element is referenced. Code is also generated for checking assigned GOTO statements. This additional code causes your program to take longer to execute. It also uses additional code space. Using Your System Language In some cases it might help efficiency to write part of your program in the system language of your machine. For example, if you have to move an entire array to another array with the same dimensions, your system language might allow you to move the array as a single block instead of moving each array element separately. If the array is large, using the system language could save a significant amount of execution time. Enabling Sudden Underflow of Denormalized Values On [...] that have implemented sudden underflow, you can [...] enable sudden underflow (flush to zero) of denormalized values. Sudden underflow will cause some floating-point applications to run faster, with a possible loss of numerical accuracy on numbers very close to zero. Efficiency Issues with Array Assignment Array assignment provides a concise and convenient notation, and in most cases it results in an efficient method of handling arrays. The execution of the array assignment (variable = expr) assigns elements of the expr to corresponding elements of the variable. If the expr is a scalar, it is treated as an array of the same shape as variable where each element has the value of expr. However, there are situations when using array assignments in which array temporaries must be allocated at run-time, possibly degrading the performance of the program. For example, array temporaries might be created when dependencies exist in an array assignment. This is because, in some cases, dependencies require that an array temporary be used to hold an intermediate result in order for the assignment to be correctly evaluated. As an example, the following assignment to reverse the elements of an array is actually executed in two distinct steps: INFO(1:100) = INFO(100:1:-1) In the first step, an array temporary is allocated and assigned the values of the section INFO(100:1:-1). In the second step, the contents of the array temporary is assigned into INFO(1:100). Array temporaries may also be required to pre-evaluate vector subscripts. For example, in the following assignment, the vector subscript IVALS may be overwritten by assignments to the left-hand-side variable IVALS: IVALS(IVALS) = 0 For this reason, the vector subscript is evaluated into an array temporary, and then this array temporary is used to represent the vector subscript. In many uses of array assignment, array temporaries do not need to be created. For example, in the following assignment, the assignment can be performed by directly evaluating the expr element by element and assigning the result to elements of X: PROGRAM MAIN REAL X(100), Y(100), Z(100) X = X * SIN(Y+Z) [...] Efficiency Issues with Array Section Actual Arguments Array temporaries are also created when passing array sections that are specified with triplet subscripts, possibly referencing non-contiguous elements of the array. For example, INTEGER DETAILS(10) DATA DETAILS/1,2,3,4,5,6,7,8,9,10/ CALL WORK(DETAILS(1:10:3)) PRINT *, 'IN MAIN', DETAILS END SUBROUTINE WORK(VALUES) INTEGER VALUES(4) PRINT *, 'IN WORK', VALUES VALUES = -1 END The output from the previous example would be: IN WORK 1 4 7 10 IN MAIN -1 2 3 -1 5 6 -1 8 9 -1 Variables used as actual arguments in FORTRAN are passed by reference. This also applies to passing array sections that are specified with triplet subscripts, possibly referencing non-contiguous elements of the array. To achieve pass-by-reference behavior, the example program handles the call as follows: * It copies the selected elements of the array section into an array temporary. In this case, the temporary is a rank one array containing the four selected elements of array DETAILS. * It passes the array temporary to subroutine WORK. The subroutine views its argument as a contiguous array of four elements, even though the actual argument consists of non-neighboring elements of DETAILS. * After the call to WORK completes, it copies the elements of the array temporary back into the selected elements of DETAILS. In this manner, any changes made to the dummy argument array VALUES in SUB are reflected in the actual argument DETAILS. This method of handling array section actual arguments is called copy-in/copy-out. Passing an array section as an argument may be well suited for a particular task, but it is important to be aware of the overhead involved. An actual argument which is an array section containing a vector subscript must not be modified by the called routine. If the called routine does modify the argument, the changes will not be reflected back in the actual argument. For example, the output from this example, INTEGER DETAILS(10) DATA DETAILS/1,2,3,4,5,6,7,8,9,10/ INTEGER INDX/1,4,7,10/ CALL WORK(DETAILS(INDX)) PRINT *, 'IN MAIN', DETAILS END SUBROUTINE WORK(VALUES) INTEGER VALUES(4) PRINT *, 'IN WORK', VALUES VALUES = -1 END would be: IN WORK 1 4 7 10 IN MAIN 1 2 3 4 5 6 7 8 9 10 Note that the selected elements of the section actual argument are passed to the subroutine, but the changes made to the dummy argument do not modify the actual argument. [...]