[CMake] History and ideas behind FindMPI.cmake?

Michael Wild themiwi at gmail.com
Wed Jan 14 04:42:35 EST 2009


On 13. Jan, 2009, at 15:17, Bartlett, Roscoe A wrote:
>
> In Trilinos, an MPI configured build creates all MPI executables (in  
> some sense).  Also, most Trilinos software is set up to  
> automatically check to see if MPI is initialized or not and will run  
> correctly in serial mode with MPI enabled but not initialized.
>
> Also, most newer MPI implementations that I have used allow you to  
> build with MPI support but never call MPI_Init(...) and never have  
> to use mpiexec/mpirun in order to run a serial executable (as long  
> as you don't call MPI functions).  That is how you can get away with  
> defining CMAKE_[C,CXX,Fortran]_COMPILER as the MPI compiler  
> wrappers.  Also, if you only use try_compile(...) tests at configure  
> time, then you will never have any problem with this.  Even with  
> try_run(...), as long as these tests don't call MPI functions then  
> they should work just fine also with most newer MPI implementations.
>
> Lastly, our project Trilinos does not really define *any*  
> executables except for tests and examples.  To set up even single- 
> process tests/examples to work with MPI is trivial.  There is a  
> single object that you have to create in main(...) like:
>
>  Teuchos::GlobalMPISession mpiSession(&argc, &argv);
>
> and you just have to run the test/example with mpirun/mpiexec with  
> one process in case your MPI implementation requires that you do so.
>
> The C++ class Teuchos::GlobalMPISession works with both MPI and non- 
> MPI enabled software.

That might be true for Trilinos, but not necessarily for other  
projects. I can easily imagine a project where the main executable/ 
library is built using MPI, but some helper/utility programs are not.  
There is no point in linking them against MPI. This not only makes  
startup slower, but also increases memory footprint. In some cases it  
might even be harmful.

>
>
>>> 4) The standard MPI install structure of many MPI implementations
>>> (e.g. MPICH, OpenMPI, LAM MPI etc.) of prefix/[include,
>> bin] will be
>>> exploited and searching from MPI_BASE_DIR (which is set by the
>>> client) will be done.  In this way, you can just set a single cache
>>> variable on most systems and get MPI totally set up.
>>
>> This won't work at all. See the --includedir and --libdir
>> options of the OpenMPI configure script. Your proposal will
>> force these values to the default, making too many assumptions.
>
> You misunderstood.  This would not require that MPI must be  
> installed in PREFIX/[include, bin, lib] but if it is, then the user  
> could just give the base directory.  It is not hard to set this up  
> at all and I am surprised that the other CMake find modules don't do  
> this.  However, if we are going to just use mpicc/mpiCC/mpiC++/ 
> mpif77 etc and mpirun/mpiexec then we just need the 'bin' directory  
> and can thrown the rest of this out.


It's not necessary. The user can define CMAKE_PREFIX_PATH and this has  
the effect you want.


>
>
>>> These days, I don't know if anyone still uses raw compilers
>> and then
>>> tries to manually pass in the compiler options and link
>> libraries.
>>> We can still support such a thing but I think this is the
>> 1% exception
>>> instead of the rule.  Am I wrong about this?
>>
>> I know of at least one large project which does not use the
>> wrappers because the developers abstracted the communications
>> into separate libraries, one for MPI, one for GAMMA and one
>> for LVM. The user has then the choice of which library should
>> be used when he starts the program. Couldn't do that with
>> your proposal.
>
> Is this a runtime option or a configure-time option in this "large  
> project"?  If it is a runtime option, then you have to at least  
> build in support for MPI so using the MPI compiler wrappers is just  
> fine.  If it is a configure-time option, then you can configure with  
> and without MPI.  That is exactly what Trilinos does.  You can build  
> a serial version of Trilinos with and without MPI.  If there was  
> some other great communication layer we could also wrap that and use  
> that with most of Trilinos (most of Trilinos uses thin abstract  
> adapters for communication and is not directly tied to MPI).

It is a runtime option. Before you start a solver you can choose which  
implementation of the communications library you want. One of the  
reasons I don't like linking against all possible parallel  
communications libraries is our heterogeneous cluster. On some compute  
nodes PVM is available, but not MPI and vice versa. There are also  
differenc MPI implementations around (OpenMPI, Quadrics, etc.) The  
queueing system automagically submits the job to the right queue.  
Problem is now, that if e.g. everything is linked against OpenMPI but  
is submitted to a queue where only PVM or Quadrics MPI is installed,  
the program won't even start.

If you really want to use an MPI compiler for everything, then you  
could use the following FindMpiCompilers.cmake module I came up with:

# - Find the MPI compiler wrappers
# This module locates the MPI compiler wrappers (mpicc, mpicxx/mpic++,  
mpif77 and mpif90).
# It is useful if one wants to force a project to use the MPI compiler  
wrappers as default
# compilers.
#
# The module has the following COMPONENTS:
#  C    searches for mpicc
#  CXX  searches for mpicxx and mpic++
#  F77  searches for mpif77
#  F90  searches for mpif90
# If no components are specified, all of them are enabled by default.
#
# The module sets the following cache variables (if the corresponding  
module is enabled):
#  MPICOMPILERS_C    the mpicc compiler
#  MPICOMPILERS_CXX  the mpicxx/mpic++ compiler
#  MPICOMPILERS_F77  the mpif77 compiler
#  MPICOMPILERS_F90  the mpif90 compiler
#
# If the user wishes to specify a specific compiler wrapper (e.g. one  
which is
# using a non-standard name or one which is not found on the path) can  
do so
# by setting the corresponding MPICOMPILERS_XXX variable (e.g. using the
# -D flag the first time CMake is run). It also honours environment  
variables
# by the same name. The CC, CXX and similar variables are not  
considered by
# design.
#
# If the module is not REQUIRED make sure to check the MPICOMPILERS_XXX
# variables.
#
# Beware that although the module can search for both the mpif77 and  
mpif90
# compiler wrappers, CMake only knows the CMAKE_Fortran_COMPILER  
variable
# which means that you can't use both of the wrappers in the same  
project. This,
# however, probably is not a big issue as Fortran90 is a superset of
# Fortran77 and all Fortran90 compilers I know of also process Fortran77
# sources.
#
# An example CMakeLists.txt could look like this:
#
#  # prevent CMake from compiler detection using NONE as the project  
language
#  project( some_project NONE )
#
#  cmake_minimum_required( VERSION 2.6 )
#
#  # find the mpi compiler wrappers
#  find_package( MpiCompilers REQUIRED CXX F77 )
#
#  # set the CMAKE_XXX_COMPILER variables
#  set( CMAKE_CXX_COMPILER ${MPICOMPILERS_CXX} )
#  set( CMAKE_Fortran_COMPILER ${MPICOMPILERS_F77} )
#  # enable the corresponding languages to do the compiler detection
#  enable_language( CXX )
#  enable_language( Fortran )
#
#  # now go on as usual
#  add_executable( fancy_mpi_program source1.cxx source2.f )

# Copyright 2009 Michael Wild <themiwi at users.sourceforge.net>

# check the components that are requested
if( MpiCompilers_FIND_COMPONENTS )
   set( __MpiCompilers_C FALSE )
   set( __MpiCompilers_CXX FALSE )
   set( __MpiCompilers_F77 FALSE )
   set( __MpiCompilers_F90 FALSE )
   foreach( __MpiCompilers_comp ${MpiCompilers_FIND_COMPONENTS} )
     if( __MpiCompilers_comp STREQUAL C )
       set( __MpiCompilers_C TRUE )
     elseif( __MpiCompilers_comp STREQUAL CXX )
       set( __MpiCompilers_CXX TRUE )
     elseif(__MpiCompilers_comp STREQUAL F77 )
       set( __MpiCompilers_F77 TRUE )
     elseif( __MpiCompilers_comp STREQUAL F90 )
       set( __MpiCompilers_F90 TRUE )
     else( __MpiCompilers_comp STREQUAL C )
       message( FATAL_ERROR "Unknown component ${__MpiCompilers_comp}" )
     endif( __MpiCompilers_comp STREQUAL C )
   endforeach( __MpiCompilers_comp )
else( MpiCompilers_FIND_COMPONENTS )
   # by default turn all components on
   set( __MpiCompilers_C TRUE )
   set( __MpiCompilers_CXX TRUE )
   set( __MpiCompilers_F77 TRUE )
   set( __MpiCompilers_F90 TRUE )
endif( MpiCompilers_FIND_COMPONENTS )

# find the requested compilers and set up the list
# of required variables
set( __MpiCompilers_REQVARS "" )
set( __MpiCompilers_FOUNDCOMPILERS "" )
if( __MpiCompilers_C )
   if( NOT "$ENV{MPICOMPILERS_C}" STREQUAL "" )
     set( MPICOMPILERS_C $ENV{MPICOMPILERS_C} CACHE FILEPATH "Path to  
the MPI C compiler" )
   else( NOT "$ENV{MPICOMPILERS_C}" STREQUAL "" )
     find_program( MPICOMPILERS_C mpicc DOC "Path to the MPI C  
compiler" )
   endif( NOT "$ENV{MPICOMPILERS_C}" STREQUAL "" )
   list( APPEND __MpiCompilers_REQVARS MPICOMPILERS_C )
   set( __MpiCompilers_FOUNDCOMPILERS "$ 
{__MpiCompilers_FOUNDCOMPILERS} ${MPICOMPILERS_C}" )
endif( __MpiCompilers_C )
if( __MpiCompilers_CXX )
   if( NOT "$ENV{MPICOMPILERS_CXX}" STREQUAL "" )
     set( MPICOMPILERS_CXX $ENV{MPICOMPILERS_CXX} CACHE FILEPATH "Path  
to the MPI C++ compiler" )
   else( NOT "$ENV{MPICOMPILERS_CXX}" STREQUAL "" )
     find_program( MPICOMPILERS_CXX NAMES mpicxx mpic++ DOC "Path to  
the MPI C++ compiler" )
   endif( NOT "$ENV{MPICOMPILERS_CXX}" STREQUAL "" )
   list( APPEND __MpiCompilers_REQVARS MPICOMPILERS_CXX )
   set( __MpiCompilers_FOUNDCOMPILERS "$ 
{__MpiCompilers_FOUNDCOMPILERS} ${MPICOMPILERS_CXX}" )
endif( __MpiCompilers_CXX )
if( __MpiCompilers_F77 )
   if( NOT "$ENV{MPICOMPILERS_F77}" STREQUAL "" )
     set( MPICOMPILERS_F77 $ENV{MPICOMPILERS_F77} CACHE FILEPATH "Path  
to the MPI F77 compiler" )
   else( NOT "$ENV{MPICOMPILERS_F77}" STREQUAL "" )
     find_program( MPICOMPILERS_F77 mpif77 DOC "Path to the MPI F77  
compiler" )
   endif( NOT "$ENV{MPICOMPILERS_F77}" STREQUAL "" )
   list( APPEND __MpiCompilers_REQVARS MPICOMPILERS_F77 )
   set( __MpiCompilers_FOUNDCOMPILERS "$ 
{__MpiCompilers_FOUNDCOMPILERS} ${MPICOMPILERS_F77}" )
endif( __MpiCompilers_F77 )
if( __MpiCompilers_F90 )
   if( NOT "$ENV{MPICOMPILERS_F90}" STREQUAL "" )
     set( MPICOMPILERS_F90 $ENV{MPICOMPILERS_F90} CACHE FILEPATH "Path  
to the MPI F90 compiler" )
   else( NOT "$ENV{MPICOMPILERS_F90}" STREQUAL "" )
     find_program( MPICOMPILERS_F90 mpif90 DOC "Path to the MPI F77  
compiler" )
   endif( NOT "$ENV{MPICOMPILERS_F90}" STREQUAL "" )
   list( APPEND __MpiCompilers_REQVARS MPICOMPILERS_F90 )
   set( __MpiCompilers_FOUNDCOMPILERS "$ 
{__MpiCompilers_FOUNDCOMPILERS} ${MPICOMPILERS_F90}" )
endif( __MpiCompilers_F90 )

mark_as_advanced( ${__MpiCompilers_REQVARS} )

# handle standard arguments
include( FindPackageHandleStandardArgs )
find_package_handle_standard_args( MpiCompilers DEFAULT_MSG  
__MpiCompilers_FOUNDCOMPILERS ${__MpiCompilers_REQVARS} )

>
>
> The argument for directly using the MPI compiler wrappers is  
> compelling (and that is why almost everyone I know uses them).

Yes, for simple projects. NOT for mixed projects.

Michael
-------------- next part --------------
A non-text attachment was scrubbed...
Name: PGP.sig
Type: application/pgp-signature
Size: 194 bytes
Desc: This is a digitally signed message part
URL: <http://www.cmake.org/pipermail/cmake/attachments/20090114/dcb638d8/attachment.pgp>


More information about the CMake mailing list