This section specifies the requirements of a C++ profiling interface to MPI.
[] Advice
to implementors.
Since the main goal of profiling is to intercept function calls from user code, it is the implementor's decision how to layer the underlying implementation to allow function calls to be intercepted and profiled. If an implementation of the MPI C++ bindings is layered on top of MPI bindings in another language (such as C), or if the C++ bindings are layered on top of a profiling interface in another language, no extra profiling interface is necessary because the underlying MPI implementation already meets the MPI profiling interface requirements.
Native C++ MPI implementations that do not have access to other profiling interfaces must implement an interface that meets the requirements outlined in this section.
High quality implementations can implement the interface outlined in
this section in order to promote portable C++ profiling libraries.
Implementors may wish to provide an option whether to build the C++
profiling interface or not; C++ implementations that are already
layered on top of bindings in another language or another profiling
interface will have to insert a third layer to implement the C++
profiling interface.
( End of advice to implementors.)
To meet the requirements of the C++ MPI profiling interface, an
implementation of the MPI functions must:
2. Ensure that those MPI functions which are not replaced may
still be linked into an executable image without causing name
clashes.
3. Document the implementation of different language bindings of
the MPI interface if they are layered on top of each other, so
that profiler developer knows whether they must implement the
profile interface for each binding, or can economize by implementing
it only for the lowest level routines.
4. Where the implementation of different language bindings is is
done through a layered approach (e.g., the C++ binding is a set of
``wrapper'' functions which call the C implementation), ensure that
these wrapper functions are separable from the rest of the library.
This is necessary to allow a separate profiling library to be correctly implemented, since (at least with Unix linker semantics) the profiling library must contain these wrapper functions if it is to perform as expected. This requirement allows the author of the profiling library to extract these functions from the original MPI library and add them into the profiling library without bringing along any other unnecessary code.
5. Provide a no-op routine MPI::Pcontrol in the MPI
library.
[] Advice
to implementors.
There are (at least) two apparent options for implementing the C++ profiling interface: inheritance or caching. An inheritance-based approach may not be attractive because it may require a virtual inheritance implementation of the communicator classes. Thus, it is most likely that implementors still cache PMPI objects on their corresponding MPI objects. The caching scheme is outlined below.
The ``real'' entry points to each routine can be provided within a namespace PMPI. The non-profiling version can then be provided within a namespace MPI.
Caching instances of PMPI objects in the MPI handles provides the ``has a'' relationship that is necessary to implement the profiling scheme.
Each instance of an MPI object simply ``wraps up'' an instance of a PMPI object. MPI objects can then perform profiling actions before invoking the corresponding function in their internal PMPI object.
The key to making the profiling work by simply re-linking programs is by having a header file that declares all the MPI functions. The functions must be defined elsewhere, and compiled into a library. MPI constants should be declared extern in the MPI namespace. For example, the following is an excerpt from a sample mpi.h file:
Example
Sample mpi.h file.
namespace PMPI { class Comm { public: int Get_size() const; }; // etc. };namespace MPI { public: class Comm { public: int Get_size() const;
private: PMPI::Comm pmpi_comm; }; };
Note that all constructors, the assignment operator, and the destructor in the MPI class will need to initialize/destroy the internal PMPI object as appropriate.
The definitions of the functions must be in separate object files; the PMPI class member functions and the non-profiling versions of the MPI class member functions can be compiled into libmpi.a, while the profiling versions can be compiled into libpmpi.a. Note that the PMPI class member functions and the MPI constants must be in different object files than the non-profiling MPI class member functions in the libmpi.a library to prevent multiple definitions of MPI class member function names when linking both libmpi.a and libpmpi.a. For example:
Example
pmpi.cc, to be compiled into libmpi.a.
int PMPI::Comm::Get_size() const { // Implementation of MPI_COMM_SIZE }
Example
constants.cc, to be compiled into
libmpi.a.
const MPI::Intracomm MPI::COMM_WORLD;
Example
mpi_no_profile.cc, to be compiled into
libmpi.a.
int MPI::Comm::Get_size() const { return pmpi_comm.Get_size(); }
Example
mpi_profile.cc, to be compiled into libpmpi.a.
int MPI::Comm::Get_size() const { // Do profiling stuff int ret = pmpi_comm.Get_size(); // More profiling stuff return ret; }
( End of advice to implementors.)