Any particular reason you need a boost::mpi::environment
instance in your classes? It is just a horrible OO wrapper around the currently singleton nature of MPI - you may initialise MPI only once by calling MPI_Init()
and finalise it only once afterwards by calling MPI_Finalize()
(at least, until the MPI sessions make it into the future versions of the standard). If you try to do anything related to MPI before MPI_Init()
, you get an error (except for a handful of information query calls that work). If you try to do anything related to MPI after MPI_Finalize()
, you get an error. If a single rank exits without calling MPI_Finalize()
after calling MPI_Init()
, the entire MPI job usually crashes as the launcher assumes something wrong has happened and kills the rest of the ranks.
The sole purpose of the boost::mpi::environment
instance is to ensure that MPI was initialised and not left un-finalised and all that is controlled by the lifetime of the object. Therefore, you have to place an instance of it where that instance will predate and outlive any MPI activity in your program, and the best place for that is the main()
function. Besides, although MPI-2 implementations are prevalent nowadays and you can safely use the parameterless variant of environment
's constructor, it's better to use the one that takes argc
and argv
from main()
for compatibility sake. Unless you are writing a library, of course.
Note that environment
objects check if MPI was already initialised by the time they are created and if so, they won't finalise it when destroyed, but they will finalise it if they were to one to initialise it. So, you should not have two instances with non-overlapping lifetimes. Therefore, you have to ensure proper constructor and destructor call order when you deal with global instances of the class. So, keep it simple, and just create one instance in the main()
function.
This is not the case with boost::mpi::communicator
. It's default constructor merely wraps the MPI_COMM_WORLD
communicator handle, which is a run-time constant referring to the singleton MPI world communicator. Since the wrapping mode of the default constructor is comm_attach
, the instance will not try to free MPI_COMM_WORLD
upon destruction, therefore you may have as many of those as you like and in any part of your code. Just keep in mind that most of the instance methods only work after initialisation and before finalisation of the MPI environment, which is what defines the lifetime of the actual world communicator object (the one in the MPI implementation, not the instance of boost::mpi::communicator
).
You don't even need a static instance of communicator
as along as you only need it for access to the world communicator. You are only saving one call to new
and one to delete
.
I would write your code simply like this:
#include <boost/mpi.hpp>
#include <iostream>
template<typename T>
class Example {
boost::mpi::communicator world;
public:
Example() {
std::cout << world.rank() << std::endl;
}
};
int main(int argc, char **argv) {
boost::mpi::environment(argc, argv);
auto e = Example<double>();
}
It works as expected:
$ mpic++ -o works works.cc -lboost_mpi
$ mpiexec -n 4 ./works
2
0
3
1