AdjoinableMPI
|
The Adjoinable MPI (AMPI) library provides a modified set of MPI subroutines that are constructed such that:
There are principal recipes for the construction of the adjoint of a given communication, see[1] . The practical implementation of these recipes, however, faces the following challenges.
The above choices imply certain consequences on the complexity for implementing the adjoint (and forward derivative) action and this could imply differences in the AMPI design. However, from a user's perspective it is a clear advantage to present a single, AD tool implementation independent AMPI library such that switching AD tools is not hindered by AMPI while also promoting a common understanding of the differentiation through MPI calls. We assume the reader is familiar with MPI and AD concepts.
The sources can be accessed through the AdjoinableMPI mercurial repository. Bug tracking, feature requests etc. are done via trac. In the following we assume the sources are cloned (cf mercurial web site for details about mercurial) into a directory AdjoinableMPI
by invoking
Configuration, build, and install follows the typical GNU autotools chain. Go to the source directory
If the sources were obtained from the mercurial repository, then one first needs to run the autotools via invoking
In the typical autoconf
fashion invoke
in or outside the source tree. The AD tool supporting AMPI should provide information which detailed AMPI configure settings are required if any. Build the libaries with
Optionally, before installing, one can do a sanity check by running: make check
.
To install the header files and compiled libraries follow with
after which in the installation directory one should find under <installation directory>
the following.
Note, the following libraries are AMPI internal:
For a given MPI-parallelized source code the user will replace all calls to MPI_... routines with the respective AMPI_... equivalent provided in User-Interface declarations. To include the declarations replace
mpi.h
with mpif.h
with respectively.
Because in many cases certain MPI calls (e.g. for initialization and finalization) take place outside the scope of the original computation and its AD-derivatives and therefore do not themselves become part of the AD process, see the explanations in Using subroutine variants NT vs non-NT relative to the differentiable section. Each routine in this documentation lists to the changes to the parameters relative to the MPI standard. These changes impact parameters specifying
Some routines require new parameters specifying the pairing two-sided communications, see Pairings. Similarly to the various approaches (preprocessing, templating, using typedef
) employed to effect a change to an active type for overloading-based AD tools, this switch from MPI to AMPI routines should be done as a one-time effort. Because AMPI provides an implementation for a straight pass-through to MPI it is possible to make this switch permanent and retain builds that are completely independent of any AD tool and use AMPI as a thin wrapper library to AMPI.
After the switch described in Switching from MPI to Adjoinable MPI is done, the application should be recompiled with the include path addition
and linked with the link path extension
Note, the name of the subdirectory (lib or lib64 ) depends on the system; the appropriate set of libraries, see Library - Configure, Build, and Install; the optional ones in square brackets depend on the AD tool:
OR if instead of differentiation by AD a straight pass-through to MPI is desired, then
instead.
All locations discussed below are relative to the top level source directory. The top level header file to be included in place of the usual "mpi.h" is located in ampi/ampi.h
It references the header files in ampi/userIF
, see also User-Interface header files which are organized to contain
ampi/userIF/passThrough.h
which exists to give the extent of the original MPI we coverampi/userIF/nt.h
ampi/userIF/modified.h
ampi/userIF/st.h
; while these impose a larger burden for moving from MPI to AMPI on the user, they also enable a wider variety of transformations currently supported by the tools; we anticipate that the ST specific versions may become obsolete as the source transformation tools evolve to support all transformations via the routines in ampi/userIF/modified.h
Additional header files contain enumerations used as arguments to AMPI routines. All declarations that are part of the user interface are grouped in User-Interface declarations. All other declarations in header files in the library are not to be used directly in the user code.
A library that simply passes through all AMPI calls to their MPI counterparts for a test compilation and execution without any involvement of and AD tool is implemented in the source files in the PlainC
directory.
The typical assumption of a program to be differentiated is that there is some top level routine head
which does the numerical computation and communication which is called from some main driver
routine. The driver
routine would have to be manually adjusted to initiate the derivative computation, retrieve, and use the derivative values. Therefore only head
and everything it references would be adjoined while driver
would not. Typically, the driver
routine also includes the basic setup and teardown with MPI_Init and MPI_Finalize and consequently these calls (for consistency) should be replaced with their AMPI "no trace/transformation" (NT) counterparts AMPI_Init_NT and AMPI_Finalize_NT. The same approach should be taken for all resource allocations/deallocations (e.g. AMPI_Buffer_attach_NT and AMPI_Buffer_detach_NT) that can exist in the scope enclosing the adjointed section alleviating the need for the AD tool implementation to tackle them. For cases where these routines have to be called within the adjointed code section the variants without the _NT
suffix will ensure the correct adjoint behavior.
Because the MPI standard passes buffers as void*
(aka choice) the information about the type of the buffer and in particular the distinction between active and passive data (in the AD sense) must be conveyed via the datatype
parameters and be consistent with the type of the buffer. To indicate buffers of active type the library predefines the following
Passive buffers can be used as parameters to the AMPI interfaces with respective passive data type values.
Because additional information has to be attached to the MPI_Request instances used in nonblocking communications, there is an expanded data structure to hold this information. Even though in some contexts (F77) this structure cannot be exposed to the user code the general approach is to declare variables that are to hold requests as AMPI_Request (instead of MPI_Request).
Following the explanations in[1] it is clear that context information about the communication pattern, that is the pairing of MPI calls, is needed to achieve
In AMPI pairings are conveyed via additional pairedWith
parameters which may be set to AMPI_PairedWith enumeration values , see e.g. AMPI_Send or AMPI_Recv. The need to convey the pairing imposes restrictions because in a given code the pairing may not be static. For a example a given MPI_Recv
may be paired with
but the AD tool has to decide on the send mode once the reverse sweep needs to adjoin the orginal MPI_Recv
. Tracing such information in a global data structure is not scalable and piggybacking the send type onto the message so it can be traced on the receiving side is conceivable but not trivial and currently not implemented.
Note that this does not prevent the use of wild cards for source, or tag.
A set of examples organized to illustrate the uses of AMPI together with setups for AD tools that also serve as regression tests are collected in AdjoinableMPIexamples
that can be obtained similarly to the AMPI sources themselves by cloning
The daily regression tests based on these examples report the results on the page linked via the main page of this documentation.