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.)