This section discusses the issue of subprograms in Digital Fortran 90 only as it relates to HPF. More general treatment of the various features for handling subprograms in Digital Fortran 90 can be found in the Fortran Language Reference Manual.
Parallel programming introduces a new complication for procedure calls: the interface between the procedure and the calling program must take into account not only the type, kind, rank, and size of the relevant objects, but also their mapping across the processors in a PSE cluster.
Digital Fortran 90 supports explicit- shape, assumed-shape, and assumed-size array specifications. Arguments passed using explicit-shape and assumed-shape dummies can be handled in parallel. Assumed-size dummies, however, will always be mapped as serial replicated.
In the current implementation, best performance is still obtained using descriptive mapping.
Descriptive syntax is shown in the following example:
PROGRAM foo
REAL T(100, 100)
REAL U(100, 100)
!HPF$ ALIGN U WITH T
!HPF$ DISTRIBUTE T(BLOCK, BLOCK)
INTERFACE
SUBROUTINE bar(R, S)
REAL R(:, :)
REAL S(:, :)
!HPF$ ALIGN S WITH *R
!HPF$ DISTRIBUTE R*(BLOCK, BLOCK)
END SUBROUTINE bar
END INTERFACE
.
.
.
CALL bar(T, U)
.
.
.
SUBROUTINE bar(R, S)
REAL R(:, :)
REAL S(:, :)
!HPF$ ALIGN S WITH *R
!HPF$ DISTRIBUTE R*(BLOCK, BLOCK)
.
.
.
END
The asterisk ( * ) in the DISTRIBUTE directive for R specifies that the array is being mapped descriptively. The current implementation treats a descriptive mapping as an assertion that the actual array is already mapped as specified at the time it is passed to the subroutine.
In this example, the actual arguments T and U must already be distributed BLOCK, BLOCK at the time it is passed to subroutine bar. You can verify this by comparing the descriptive DISTRIBUTE directive for R in the subroutine and interface block with the DISTRIBUTE directive for T in the main program.
The descriptive mapping of a dummy argument in a called routine (such as A in the example in Section 6.4.2) must be visible to the calling routine in an explicit interface. An explicit interface consists of one of the following:
Note that three sets of declarations were required for the arguments in the example code given in Section 6.4.2:
This duplication of information in the interface block can be avoided through the use of modules (see Section 6.4.4).
The colons in the declarations of the dummies R and S indicate that they are assumed shape. They take ("assume") the shape of the actual arrays passed to them at the CALL statement.
The asterisks in the ALIGN and DISTRIBUTE statements for the dummies R and S indicate descriptive data mapping. In the current implementation, a descriptive mapping is an assertion that the actual arrays, at the time they are passed to the subroutine, are already mapped as specified. This information allows the array to be passed without being re-mapped.
In the current implementation, you must take care that descriptive data mappings are accurate. An incorrect descriptive data mapping will cause the program to fail or generate incorrect results.
In summary, the following three conditions currently constitute the preferred method of passing arguments in HPF programs when motion of data is not desired:
In future releases, Digital intends to eliminate the need for using descriptive syntax, in conformance with Version 2.0 of the High Performance Fortran Language Specification. This should not require any changes to programs currently using descriptive syntax.
Modules replace the old BLOCK DATA and COMMON techniques for passing data to subprograms.
Module program units are useful for structuring a program into libraries of:
Following is an example of a module that contains a procedure that can be called from a main program with a USE statement:
MODULE FUNCTION_TO_BE_INTEGRATED
CONTAINS
PURE REAL FUNCTION F(X) ! FUNCTION TO BE INTEGRATED
REAL, INTENT(IN) :: X
F = 4 / (1.0 + X**2)
END FUNCTION F
END MODULE FUNCTION_TO_BE_INTEGRATED
An example of the use of modules can be seen in Chapter 5.
In HPF, a pure function or pure subroutine is one that produces no side effects and makes no reference to mapped variables other than its actual argument. This means that a pure function's only effect on the state of a program is to return a value, and a pure subroutine's only effect on the state of a program is to modify INTENT(OUT) and INTENT(INOUT) parameters.
User-defined functions may be called inside a FORALL structure only if they are pure functions. Subroutines called by PURE functions must be pure. Because a FORALL structure is an extended assignment statement (not a loop), there is no way to directly express a subroutine call from within a FORALL structure; however, a PURE function that is called within a FORALL structure may itself call a pure subroutine.
Assigning the PURE attribute to a function allows that function to be called inside a FORALL structure. Assigning the PURE attribute to a subroutine lets that subroutine be called inside a PURE function.
The PURE attribute is required only for functions called within a FORALL structure. Functions called in Fortran 90 array assignment statements or INDEPENDENT DO loops do not need to be pure.
The PURE attribute was designed to avoid two separate problems that are otherwise possible in FORALL structures:
Therefore, constraints on PURE functions and subroutines include restrictions on both side effects and data mapping. The features necessary to permit a function or subroutine to be assigned the PURE attribute in Digital Fortran 90 include:
Some additional prohibitions apply to PURE functions that do not apply to PURE subroutines. Most notably, a PURE subroutine may modify its arguments, whereas a PURE function may not do so. These additional prohibitions are listed in the High Performance Fortran Language Specification as the third constraint in Section 4.3.1.1.
Because PURE functions and subroutines, like all Fortran functions and subprograms, may be compiled separately, the compiler has no way of evaluating the accuracy of a program's assertion that a procedure is PURE. The programmer must take responsibility for checking these conditions. Illegal use of the PURE attribute is not detected by the compiler, and may result in incorrect program results.
The following is an example use of the PURE attribute:
PURE FUNCTION DOUBLE(X) REAL, INTENT(IN) :: X DOUBLE = 2 * X END FUNCTION DOUBLE
Transcriptive mapping is to handle the case when the data mapping of arguments is not known to the sub-program at compile time. The compiler makes a generalized version of the subprogram that can accept whatever mapping the arguments have when passed in.
Transcriptive distribution is specified with an asterisk. For example:
!HPF$ DISTRIBUTE A *
This specifies that the dummy argument should be distributed in the same way as the actual.
There is no transcriptive form of the ALIGN directive. Transcriptive alignment is specified with the INHERIT directive. The INHERIT attribute specifies that a dummy argument should be aligned in the same way as the actual, if the actual has been named in an ALIGN directive.
Using transcriptive mappings forces the compiler to generate code that is generalized for any possible alignment, which may be less efficient. The best performance is usually obtained with descriptive mappings.
The following example shows two ways of passing a mapped actual to a subroutine, one with transcriptive mapping, and one with descriptive mapping:
! With Transcriptive Mapping | ! With Descriptive Mapping
! -------------------------- | ! ---------------------------------
PROGRAM foo | PROGRAM foo
INTEGER T(100, 100) | INTEGER T(100, 100)
INTEGER U(50, 50) | INTEGER U(50, 50)
!HPF$ DISTRIBUTE T(BLOCK, BLOCK) | !HPF$ DISTRIBUTE T(BLOCK, BLOCK)
!HPF$ ALIGN U(I,J) WITH T(I+50,J+50)| !HPF$ ALIGN U(I,J) WITH T(I+50,J+50)
. | .
. | .
. | .
|
CALL bar(U) | CALL bar(T, U)
. | .
. | .
. | .
|
CONTAINS | CONTAINS
|
SUBROUTINE bar(R) | SUBROUTINE bar(Q, R)
INTEGER R(:, :) | INTEGER Q(:, :)
!HPF$ DISTRIBUTE R * | INTEGER R(:, :)
!HPF$ INHERIT R | !HPF$ DISTRIBUTE Q *(BLOCK, BLOCK)
. | !HPF$ ALIGN R(I, J) WITH *Q(I+50, J+50)
. | .
. | .
. | .
|
END SUBROUTINE bar | END SUBROUTINE bar
END PROGRAM foo | END PROGRAM foo
In the preceding example, the array U was aligned with another array T. If U were aligned with a template, instead of an array, the template could not be passed as an argument to the subroutine when descriptive ALIGN directives are used, because templates cannot be passed through the interface (see Section 6.3.4). When descriptive ALIGN directives are used, the template must be declared in the subroutine.
However, when the INHERIT directive is used, there is no need to declare the template in the subroutine. See the following example:
! With Transcriptive Mapping | ! With Descriptive Mapping
! -------------------------- | ! ---------------------------------
PROGRAM foo | PROGRAM foo
!HPF$ TEMPLATE T(100, 100) | !HPF$ TEMPLATE T(100, 100)
INTEGER U(50, 50) | INTEGER U(50, 50)
!HPF$ DISTRIBUTE T(BLOCK, BLOCK) | !HPF$ DISTRIBUTE T(BLOCK, BLOCK)
!HPF$ ALIGN U(I,J) WITH T(I+50,J+50)| !HPF$ ALIGN U(I,J) WITH T(I+50,J+50)
. | .
. | .
. | .
|
CALL bar(U) | CALL bar(U)
. | .
. | .
. | .
|
CONTAINS | CONTAINS
|
SUBROUTINE bar(R) | SUBROUTINE bar(R)
INTEGER R(:, :) | INTEGER R(:, :)
!HPF$ DISTRIBUTE R * | !HPF$ TEMPLATE Q(100, 100)
!HPF$ INHERIT R | !HPF$ DISTRIBUTE Q (BLOCK, BLOCK)
. | !HPF$ ALIGN R(I, J) WITH *Q(I+50, J+50)
. | .
. | .
. | .
|
END SUBROUTINE bar | END SUBROUTINE bar
END PROGRAM foo | END PROGRAM foo
Note that in the "Descriptive Mapping" example, the "DISTRIBUTE Q" line is written without an asterisk. This is because Q is local to the subroutine.
Note also that in the template declaration, it was necessary to use constant values (100, 100) rather than assumed-shape syntax (:, :) because templates cannot be passed through the interface. This, of course, requires the subroutine to be more specific to the particular program, and less general.