Optimieren von FORTRAN-Code

Ausschnittsweise wird hier aus [HP LaserRom] zitiert, um sich darunter besser etwas vorstellen zu können, was so alles berücksichtigt werden will, um effizient in FORTRAN zu programmieren.


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.

[...]


Zurück mit dem BACK-Buttons [ <|=o ] ihres Browsers.