Previous | Contents | Index |
KAP assertions enable the programmer to provide KAP with information about the program that would not normally be known at compilation time. Although many KAP users run the product without assertions, sometimes assertions can improve the optimization results. Use assertions only where speed is essential and you understand the application program well.
KAP does not guarantee that an assertion will have an effect. The information provided by the assertion will be noted, and if that information will help, it will be used.
To understand the process KAP uses in interpreting assertions, it is necessary to understand assumed dependencies. In the following loop, where X is an array, n and m are scalars, and nothing is known about the relationship between n and m , there are two types of dependencies:
DO 10 i=1,n 10 X(i) = X(i-1) + X(m) |
Between X(i) and X(i-1) there is a FORWARD dependence, and the distance is known to be one. Between X(i) and X(m) , KAP tries to find a relation, but cannot, because it does not know the value of m in relation to n . The second dependence is called an ASSUMED dependence, because it is assumed but cannot be proven to exist.
Assertions can be unsafe, because KAP cannot check the correctness of the information provided. If you specify an incorrect assertion, then the KAP generated code may give very different results than the original program. If unsafe assertions are the suspected cause of a misbehaving program, all assertions can be ignored (treated as comments) by using the -directives command-line switch without the a switch or the !*$* no assertions directive.
As with directives, an assertion placed before any comments or statements in the program is treated as a global assertion. That is, it is treated as if it were repeated at the top of each program unit in the file. Some assertions, for example, !*$* assert relation or !*$* assert permutation , include variable names. If these are specified as global assertions, the assertion is used in a program only when those variable names appear in common blocks or are dummy argument names to the subprogram. Global assertions cannot be used to make relational or permutation assertions about variables that are local to a subprogram.
Many assertions, like directives, are active until the end of the program or until overridden by another assertion. Other assertions are in effect only for the DO loops before which they appear. This type of assertion would apply to the next DO loop, but not to its nested loops. Other assertions are active within a program unit, regardless of where they appear in that program unit.
You can apply assertions collectively to a series of loops and arrays by enclosing them in a directive block. Because KAP treats the directive block as one loop, the assertions you want to be active on the loops inside the block must immediately precede the !*$* beginblock directive. Assertions immediately preceding directive blocks override previously set directives and assertions for the duration of the block, as follows:
Program Unit A !*$* directive | | !*$* assertion | | !*$* beginblock | | loop | | array | | loop | ->!*$* endblock | -> End |
If you want to use different assertions for individual loops and arrays, enclose each loop or array in its own directive block.
Table 6-1 lists KAP assertions and their duration:
Assertion | Duration |
---|---|
!*$* assert [no]argument aliasing Section 6.2.1 | until reset |
!*$* assert [no]bounds violations Section 6.2.2 | until reset |
!*$* assert concurrent call Section 6.3.1 | next loop |
!*$* assert do (concurrent) Section 6.3.2 | next loop |
!*$* assert do (concurrent call) Section 6.3.3 | next loop |
!*$* assert do (serial) Section 6.3.4 | see text |
!*$* assert do prefer (concurrent) Section 6.3.5 | next loop |
!*$* assert do prefer (serial) Section 6.3.6 | next loop |
!*$* assert [no]equivalence hazard Section 6.2.3 | until reset |
!*$* assert [no]last value needed Section 6.2.4 | until reset |
!*$* assert permutation ( <name> ) Section 6.2.5 | next loop |
!*$* assert no recurrence ( <name> ) Section 6.2.6 | next loop |
!*$* assert relation (<name>.xx.<variable/constant>) Section 6.2.7 | next loop |
!*$* assert no sync Section 6.2.8 | next loop |
!*$* assert [no] temporaries for constant arguments Section 6.2.9 | until reset |
For the assertions listed with ( name ), ( name .xx. variable ), and ( name .xx. constant ), the following example illustrates the format of the information required:
!*$* assert permutation (ip) !*$* assert relation (n .gt. m) !*$* assert relation (n .gt. 0) |
The following sections describe some KAP assertions. Additional
assertions are described in Section 6.3.
6.2.1 !*$* assert [no]argument aliasing
The !*$* assert no argument aliasing assertion allows KAP to make assumptions about subprogram arguments in a program. According to the Fortran 77 standard, multiple-aliasing of variables is allowed only if no aliases are modified. In the following code example, the subroutine violates the standard, because variable A is multiple-aliased in the subroutine through C and D , and variable X is multiple-aliased through X and E :
COMMON X,Y REAL A,B CALL SUB ( A, A, X ) ... SUBROUTINE SUB ( C, D, E ) COMMON X, Y X = ... C = ... ... |
If multiple-aliasing is used in a program, the
!*$* assert argument aliasing
assertion should be used. The command-line switch
-assume=a
acts like a global
!*$* assert argument aliasing
assertion. An argument aliasing assertion is active until reset, or
until the end of the program unit.
6.2.2 !*$* assert [no]bounds violations
The !*$* assert bounds violations assertion indicates that array subscript bounds may be violated during execution. If the user has not violated array subscript bounds, this assertion should not be used. A bounds violations assertion is active until reset or until the end of the program. For formal parameters, KAP treats a declared last dimension of (1) the same as (*).
The -assume=b command-line switch acts like a global !*$* assert bounds violations assertion.
In the following example, the first loop nest is assumed to be standard-conforming, so the loops can both be optimized. The loops can be interchanged to improve memory referencing, because no A(I,J) will overwrite an A(I',J+1) . In the second nest, the assertion warns KAP that the loop limit of the first array index (I) may violate the declared array bounds. KAP is cautious and optimizes only the right array index.
DO 100 I = 1,M DO 100 J = 1,N A(I,J) = A(I,J) + B (I,J) 100 CONTINUE C !*$*ASSERT BOUNDS VIOLATIONS DO 200 I = 1,M DO 200 J = 1,N A(I,J) = A(I,J) + B (I,J) 200 CONTINUE |
Becomes:
DO 2 J=1,N DO 2 I=1,M A(I,J) = A(I,J) + B (I,J) 2 CONTINUE C !*$* ASSERTBOUNDSVIOLATIONS DO 4 I=1,M DO 3 J=1,N A(I,J) = A(I,J) + B (I,J) 3 CONTINUE 4 CONTINUE |
KAP always assumes that array references will be within the array itself, so the rightmost index will be safe to modify references to. |
The !*$* assert noequivalence hazard assertion tells KAP that equivalenced variables will not be used to refer to the same memory location inside one DO loop nest. Normally, equivalence statements allow different variable names to refer to the same storage location. The -assume=e command-line switch acts like a global !*$* assert equivalence hazard assertion. The equivalence hazard assertion is active until reset or until the end of the program.
In the following example, if arrays E and F are equivalenced, but you know that the overlapping sections will not be referenced in this loop, using !*$* assert noequivalence hazard allows KAP to optimize the loop:
EQUIVALENCE ( E(1), F(101) ) !*$* ASSERT NO EQUIVALENCE HAZARD DO 10 I = 1,N E(I+1) = B(I) C(I) = F(I) 10 CONTINUE |
Frequently when a scalar is assigned in a loop that is optimized, KAP uses a temporary variable within the optimized loop and assigns the last value to the original scalar if KAP believes that the scalar may be reused before it is assigned again. The !*$* assert nolast value needed assertion lets KAP assume that such last-value assignments are unnecessary. This assertion is active until reset or until the end of the program.
The -assume=l command-line switch acts like a global !*$* assert last value needed assertion.
At
-optimize=2
and higher, KAP performs variable lifetime analysis to determine when
last-value assignments are unnecessary. The
!*$* assert nolast value needed
assertion can be used on a loop-by-loop basis when you do not want to
eliminate last-value assignments globally.
6.2.5 !*$* assert permutation
The !*$* assert permutation assertion provides KAP with sufficient information to allow KAP to generate optimized code for certain types of indirect addressing (INTEGER arrays used in subscripts). This assertion requires -optimize=4 or higher. The following example shows when it is unsafe to optimize a DO loop:
DO 100 I = 1,N A(IP (I)) = A(IP (I)) + B(I) 100 CONTINUE |
KAP cannot safely generate optimized code because it cannot tell if the different values in the index array IP overlap. If all values in I are distinct, the optimized code is correct; if some of the values are the same, the optimized code may be unsafe. You can tell KAP that the values in the index array IP are all distinct with the following assertion:
!*$*ASSERT PERMUTATION( IP ) DO 100 I=1,N A(IP (I)) = A(IP (I)) + B(I) 100 CONTINUE |
With the addition of this assertion, KAP knows that it can safely
generate optimized code for this loop. If at run time the values of
IP
are not distinct, the optimized code may generate incorrect results.
6.2.6 !*$* assert no recurrence
The !*$* assert no recurrence assertion asks KAP to ignore ALL data dependence conflicts due to the named variable in the following DO loop. KAP makes the final decision whether or not to ignore a data dependence conflict. The following example asks KAP to ignore all dependence arcs caused by the variable X in the loop:
!*$* ASSERT NO RECURRENCE ( X ) DO 10 I=1,M,5 10 X(K) = X(K) + X(I) |
Not only does KAP ignore the assumed dependence, but also the real dependence caused by X(K) appearing on both sides of the assignment.
This assertion only applies to the next DO loop. It cannot be specified
as a global assertion.
6.2.7 !*$* assert relation ( <name> .XX. <variable/constant>)
The !*$* assert relation (where XX=GT,LT,LE, etc) ( <name> .XX. <variable/constant> ) assertion indicates the relationship between two variables or between a variable and a (possibly signed) constant. When attempting to optimize a loop, KAP occasionally asks about such relationships, as in the following example:
DO 100 I = 4,N A(I) = A(I+M) + B(I) 100 CONTINUE |
KAP generates this question in the source code section of the listing file:
Is "M .GE. N" in this loop? |
If M will always be greater than N at this point in the program, you can relay this information to KAP using an assertion.
Again, note that KAP cannot check the validity of assertions. With the assertion that M is greater than the loop upper bound, KAP can generate optimized code for this loop. If at run time M turned out to be less than N , the results produced by the optimized code would probably be different than the results produced by the original loop.
This assertion is in effect only for the DO loop before which it
appears.
6.2.8 !*$* assert no sync
If a loop has been optimized by KAP and you note that KAP was cautious and added unnecessary synchronization code, as shown in the following example, the !*$* assert no sync assertion may improve run-time speedup.
DO 20 I = 1,N A (100 + I) = I C (100 + I) = A (I) D (100 + I) = C (I) 20 CONTINUE |
KAP standard optimization synchronizes the loop by breaking it up in order to ensure the assignments occur in a valid order. However, this maximizes DO loop overhead as follows:
DO 2 I=1,N A(I+100) = I 2 CONTINUE DO 3 I=1,N C(I+100) = A(I) 3 CONTINUE DO 4 I=1,N D(I+100) = C(I) 4 CONTINUE |
If the value of N will always be less than or equal to 100, you can eliminate the synchronization overhead by adding !*$* assert no sync as follows:
!*$* ASSERT NO SYNC DO 20 I=1,N A(I+100) = I C(I+100) = A(I) D(I+100) = C(I) 20 CONTINUE |
Sometimes, KAP transformations are disabled when KAP is not sure about their effect on the rest of the program. For example, one possible transformation would turn:
SUBROUTINE X(I,N) IF (I .LT. N) I = N END |
Into:
SUBROUTINE X(I,N) I = MAX(I,N) END |
But, if the actual parameter for I were a constant, CALL X(1,N) , it would appear that the value of the constant 1 was being reassigned. (In some older versions of Fortran, the values of constants could be changed in this way.) Without additional information, KAP is cautious and performs no argument-changing transformations within the subroutine.
Most compilers automatically put constant actual arguments into
temporary variables to protect against this case. The assertion
!*$* assert temporaries for constant arguments
or the command switch
-assume=c
(the default) inform KAP that constant parameters are protected. The
assertion
!*$* assert no temporaries for constant arguments
directs KAP to avoid transformations that might change the values of
constant parameters.
6.3 Parallel Processing Assertions that Guide Automatic Parallelization
The following sections describe assertions available in the
multiprocessor version of KAP.
6.3.1 !*$* assert concurrent call
The !*$* assert concurrent call assertion tells KAP the subroutine calls and function references in the loop immediately following this assertion can execute in parallel. It causes KAP to ignore all potential data dependencies due to subroutine arguments. This assertion does not apply to nested or surrounding loops.
The following code example shows how !*$* assert concurrent call works:
!*$* ASSERT CONCURRENT CALL DO 10 I=.. ... CALL S1 ... 10 CONTINUE |
You can use !*$* assert concurrent call to override default preprocessor action. In the following code example, !*$* assert concurrent call tells the compiler it is safe to make a parallel call to TEST. Another assertion, !*$* assert prefer do (concurrent) , tells the compiler to "prefer" a reordered loop nesting that causes parallel execution over the J loop.
!*$* ASSERT CONCURRENT CALL !*$* ASSERT PREFER DO (CONCURRENT) DO J = 1,N,64 DO K = 1,N,64 DO I = 1,N,64 CALL TEST('N','N', & MIN0(64, N-I+1),MIN0(64,N-J+1),MIN0(64,N-K+1), & 1.0D0, & A(I,K),N+1, & B(K,J),N+1, & 1.0D0, & C(I,J),N+1 & ) ENDDO ENDDO ENDDO |
The !*$* assert concurrent call assertion tells KAP to assume that all external subroutine/function calls are thread-reentrant. This will override KAP default behavior, which is to assume that all external subroutines/functions are NOT thread-reentrant or thread-safe. If the external subroutine/functions are not thread-safe, and you use !*$* assert concurrent call , your program may not execute correctly. For example, local variables in subroutines are thread-safe only if they are stored as thread-specific data. For more information on thread-safe subroutines, see the Tru64 UNIX Guide to the POSIX Threads Library. |
For more information about !*$* assert prefer do (concurrent) , see Section 6.3.5.
KAP does not generate parallel code if you use the
-noconcurrent
command-line switch.
6.3.2 !*$* assert do (concurrent)
The !*$* assert do (concurrent) assertion tells KAP to prefer to ignore assumed dependencies and to execute the DO loop immediately following this assertion in parallel.
This assertion says nothing about the concurrency threshold for the loop. This means KAP continues to honor the dependencies it finds. For example, in the following loop KAP ignores !*$* assert do (concurrent) because there is a dependence on A :
!*$* ASSERT DO (CONCURRENT) DO 100 I = 4, N A (I) = A (I-4) + B (I) 100 CONTINUE |
Strictly speaking, you could parallelize the loop enabling KAP to put the entire loop body inside a critical section. The parallelized loop would run more slowly than the original serial version, however.
The following code example shows how !*$* assert do (concurrent) tells KAP to ignore an assumed dependence, the I+M array index, and to make the stride-1 loop concurrent:
program test real a(100,100) read *,m,n read *,a !*$*assert do (concurrent) do 11 i = 1,n do 12 j = 1,n a(i,j) = a(i+m,j) 12 continue 11 continue end |
Using the assertion and processing with -unroll=1 and -conc , KAP generates the following code:
PROGRAM TEST REAL A(100,100) READ *, M, N READ *, A !*$* ASSERT DO( CONCURRENT ) !$OMP PARALLEL SHARED (N,A,M) PRIVATE (I,J) !$OMP DO DO I=1,N DO J=1,N A(I,J) = A(I+M,J) END DO END DO !$OMP END DO NOWAIT !$OMP END PARALLEL END |
Another example of when to use !*$* assert do (concurrent) follows. The DO loop can execute safely in parallel, given the value of M is greater than the value of N . Preceding the loop with !*$* assert do (concurrent) allows parallel execution to happen.
!*$* ASSERT DO (CONCURRENT) DO I = 1,N X(I) = X(I+M) ENDDO |
KAP does not generate parallel code if you use the
-noconcurrent
command-line switch.
6.3.3 !*$* assert do (concurrent call)
The
!*$* assert do (concurrent call)
assertion tells KAP to execute both the
!*$* assert do (concurrent)
and the
!*$* assert concurrent call
assertions in the immediately following loop.
6.3.4 !*$* assert do (serial)
The !*$* assert do (serial) assertion forces the loop immediately following this assertion to be serial. Additionally, it restricts optimization by forcing all enclosing loops to be serial. Inner loops and other loops inside the same enclosing loop nest, but not enclosing the serial loop, may be optimized. The following code example shows how !*$* assert do (serial) works:
DO 100 I=1,N DO 100 J = 1, N !*$* ASSERT DO (SERIAL) DO 200 K = 1, N X(I,J,K) = X(I,J,K) * Y(I,J) 200 CONTINUE DO 300 K = 1, N X(I,J,K) = X(I,J,K) + Z(I,K) 300 CONTINUE 100 CONTINUE |
The assertion forces the DO 100 I loop, the DO 100 J loop, and the DO 200 K loop to be serial. The DO 300 K loop can still be parallelized. In this case, KAP does NOT distribute the I or J loops to try to get a larger optimizable loop.
The following loop code shows an example of when to use !*$* assert do (serial) . X and Y must not process in parallel because they are equivalenced and they overlap in memory. Using !*$* assert do (serial) stops parallel execution of the loop.
!*$* ASSERT DO (SERIAL) DO I = 1,N X(I) = Y(I) ENDDO |
See also the
!*$* assert do prefer (concurrent)
assertion, Section 6.3.5, and
!*$* assert do prefer (serial)
, Section 6.3.6.
6.3.5 !*$* assert do prefer (concurrent)
The !*$* assert do prefer (concurrent) assertion tells KAP to prefer parallel ordering for the DO loop immediately following this assertion. The following code example shows how the assertion works:
!*$* ASSERT DO PREFER (CONCURRENT) DO 10 I = 1, M DO 10 J = 1, N 10 X(I,J) = X(I,J) + Y(I,J) |
The assertion tells KAP to prefer any ordering where the DO 10 I loop is parallel.
The !*$* assert do prefer (concurrent) assertion does not mention anything about assumed dependencies. If KAP finds dependencies, it does not perform optimization.
This assertion is valid only for the DO loop it precedes. The
-noconcurrent
switch disables the generation of parallel code.
6.3.6 !*$* assert do prefer (serial)
The !*$* assert do prefer (serial) assertion tells KAP to prefer serial ordering for the DO loop immediately following this assertion. Unlike !*$* assert do (serial) , assert do prefer (serial) does not inhibit optimization of outer loops.
The following code example shows how !*$* assert do prefer (serial) works:
DO 100 i=1,N DO 100 J = 1, N !*$* ASSERT DO PREFER (SERIAL) DO 200 K = 1, N X(I,J,K) = X(I,J,K) * Y(I,J) 200 CONTINUE DO 300 K = 1, N X(I,J,K) = X(I,J,K) + Z(I,K) 300 CONTINUE 100 CONTINUE |
The !*$* assert do prefer (serial) assertion allows optimization over the whole loop nest, while trying to keep the DO 200 K loop serial. Compare this example with the !*$* assert do (serial) example to see how the two assertions produce different results with identical loop structures.
Previous | Next | Contents | Index |