I would like to create a program that would work with both MPI and without at run time and I have reached a point where I think that is not possible.
For example, an MPI program might look like this:
#include <mpi.h>
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
...
MPI_Finalize ();
return 0;
}
But in order to allow users who don't have MPI installed to compile this, I would have to wrap it around #if
's:
// Somewhere here, pull in a .h file which would set HAVE_MPI to 0 or 1
#if HAVE_MPI
#include <mpi.h>
#endif
int main(int argc, char* argv[])
{
#if HAVE_MPI
MPI_Init(&argc, &argv);
#endif
...
#if HAVE_MPI
MPI_Finalize ();
#endif
return 0;
}
Until now, I think I'm correct? If this program compiled to a.out
with HAVE_MPI
set to 1
, it would be run as: mpirun -np 4 ./a.out
or mpirun -np 1 ./a.out
. It could never be run as ./a.out
, because even if it isn't run within mpirun
, it would call the MPI_*
functions.
I guess to achieve what I would like -- an executable that could be run with and without mpirun
is not possible. And the only option is to create two separate executables -- one with HAVE_MPI
set to 0 and another set to 1. Is this correct or am I missing something about how C programs using MPI are implemented?
As a side-note, I don't think this is true with shared memory parallelization (i.e., with OpenMP). The same executable should work with a single or multiple threads.
Is my understanding correct?
CodePudding user response:
A compiled MPI program needs MPI libraries at runtime in addition to the mpirun
call (not required by all MPI implementations for 1 process nor in all cases). Thus, to run MPI function only in some cases at runtime without having a dependency to MPI, the potion of the code using MPI needs to be dynamically loaded. This can be done by putting MPI-related functions in shared library components and loading them at runtime regarding your needs.
The same thing is also true for OpenMP: an OpenMP code requires OpenMP compiler flags like -fopenmp
creating a dependency to an OpenMP implementation required at runtime. That being said, the common GOMP (GCC) runtime or the IOMP runtime (ICC/Clang) are often installed on most Linux computing machines by default (especially the former since GCC is generally the default compiler and is bundled with GOMP).
Using shared library help you to easily switch between two parallel implementations: MPI and OpenMP. That being said, it means the initialization, finalization and communication collectives must be wrapped in an external library compiled separately of your program. The library functions can be loaded dynamically at startup time or manually at runtime using dlopen
(see this related post).
As pointed out in the comments, using preprocessor directives are parsed at compile time and this is why you need to rebuild your program twice when using them. This is not required with shared libraries (though all diverging parts to be executed at runtime needs to be compiled separately).