6.4 Subprograms in HPF

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.

6.4.1 Explicit-Shape, Assumed-Shape, and Assumed-Size Array Specifications

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.

For More Information:

6.4.2 Descriptive Mapping

In the current implementation, best performance is still obtained using descriptive mapping.


Note
In future releases, Digital intends to eliminate the need for 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.

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.


Note
In the current implementation, the compiler does not take any corrective action in the event that a descriptive mapping of a dummy does not match the distribution of the actual. No checking is done to ensure the accuracy of descriptive mappings. Programs with inaccurate descriptive mappings will fail to execute or generate incorrect results. 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.

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.

6.4.3 Explicit Interfaces

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:

  1. The calling routine may contain an interface block describing the called routine. The interface block must contain dummy variable declarations and mapping directives that match the routine it describes.
  2. The calling routine may contain a USE statement referring to a module that contains an explicit interface for the called routine.
  3. The calling routine may call a routine contained in the same scope.

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.

6.4.4 MODULE

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.

6.4.5 PURE Attribute

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

6.4.6 Transcriptive Distributions and the INHERIT Directive

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.


Note
In future releases, Digital intends to eliminate the need for descriptive syntax, in conformance with Version 2.0 of the High Performance Fortran Language Specification. See Section 6.4.2.