【问题标题】:how to compile MPI and non-MPI version of the same program with automake?如何使用 automake 编译同一程序的 MPI 和非 MPI 版本?
【发布时间】:2010-10-19 13:10:01
【问题描述】:

我有一个可以使用 MPI 支持编译的 C++ 代码,具体取决于 某些预处理器标志;缺少适当的标志,来源 编译成非并行版本。

我想设置 Makefile.am 以便它编译 both MPI 并行顺序版本,如果可以选择 ./configure 已给出。

这里有一个问题:MPI 有自己的 C++ 编译器包装器,并且坚持 使用它而不是标准来编译和链接源 C++ 编译器。如果我要自己编写 Makefile,我将不得不 做这样的事情:

myprog.seq: myprog.cxx
    $(CXX) ... myprog.cxx

myprog.mpi: myprog.cxx
    $(MPICXX) -DWITH_MPI ... myprog.cxx

有没有办法告诉 automake 它必须改用 $(MPICXX) 编译启用 MPI 的程序版本时的 $(CXX)?

【问题讨论】:

  • 您是否关心串行程序是否与“同时构建”情况下的 MPI 库链接?如果没有,您可以同时使用 mpicxx —— 它不会伤害任何东西。如果您不构建 mpi 版本,则可以为所有内容设置使用 g++。根据经验,这是许多同时具有并行和串行版本的软件包似乎都在做的事情——我想到了 hdf5。
  • 一般来说不会,我以前也是这样编译的。不过,我开发了一些 PMPI 工具,通常您不想将拦截器库链接到 MPI,如果您使用 mpicc 编译,您将不得不这样做。

标签: mpi autoconf automake


【解决方案1】:

我有同样的问题,我发现没有真正好的方法让自动工具有条件地为特定目标使用 MPI 编译器。 Autotools 擅长根据您的源代码编写的语言(CCCXXFCF77 等)确定要使用的编译器,但它确实不擅长弄清楚是否对特定目标使用 MPI 编译器。您可以设置 MPICC、MPICXX 等,但如果您以这种方式使用编译器,则基本上必须为您的目标重写所有 Makefile 规则(如上所述)。如果你这样做了,写一个 automake 文件有什么意义呢?

其他人建议像使用外部库一样使用 MPI,这是我提倡的方法,但您不应该手动操作,因为不同的 MPI 安装具有传递给编译器的不同标志集,并且它们可能取决于您正在编译的语言。

好消息是我所知道的所有当前发布的 MPI 编译器都支持自省参数,例如 -show-show-compile-show-link。您可以自动从脚本中提取参数。

所以,我处理这个问题的方法是制作一个m4 脚本,该脚本从 MPI 编译器中提取定义、包含、库路径、库和链接器标志,然后将它们分配给您可以在您的Makefile.am。这是脚本:

lx_find_mpi.m4

这使得 MPI 以 automake 期望的方式工作。顺便说一句,这是 CMake 在他们的 FindMPI 模块中使用的方法,我发现它在那里工作得很好。它使构建更加方便,因为您可以为您的目标做这样的事情:

bin_PROGRAMS = mpi_exe seq_exe

# This is all you need for a sequential program
seq_exe_SOURCES = seq_exe.C

# For an MPI program you need special LDFLAGS and INCLUDES
mpi_exe_SOURCES = mpi_exe.C
mpi_exe_LDFLAGS = $(MPI_CXXLDFLAGS)

INCLUDES = $(MPI_CXXFLAGS)

其他语言也有类似的标志,因为就像我说的那样,特定的标志和库可能会根据您使用的语言的 MPI 编译器而有所不同。

lx_find_mpi.m4 还设置了一些 shell 变量,以便您可以在 configure.ac 文件中测试是否找到了 MPI。例如,如果您正在寻找 MPI C++ 支持,您可以测试 $have_CXX_mpi 以查看宏是否找到它。

我已经使用mvapichOpenMPI 测试了这个宏,以及BlueGene 机器上的自定义MPICH2 实现(尽管它没有解决您将在那里看到的所有交叉编译问题) .让我知道如果有什么不起作用。我想保持宏尽可能健壮。

【讨论】:

  • 你应该将它添加到 LDADD 而不是 LDFLAGS 否则一些链接器会抛出错误。并且脚本实际有一个错误:使用 sed,要删除尾随空格,您应该使用 sed/ +/ /g 而不是 sed/ */ /g。
  • 你的宏对我来说运行良好。但是,我必须运行两次才能从 MPICC 和 MPICXX 中获取值。在第二次调用中,我将宏包装在AC_LANG_PUSH([C++])AC_LANG_POP([C++]) 之间。感谢 SW。
【解决方案2】:

很抱歉,让 automake 使用 MPI 非常困难。我已经为此苦苦挣扎了好几个月,试图找到一个好的解决方案。我有一个源代码树,它有一个库,然后在使用该库的子文件夹中有许多程序。一些文件夹是 mpi 程序,但是当我尝试用 Makefile.am 中的 MPI 编译器替换 CXX 时。

if USE_MPI
  MPIDIR = $(MPICOMPILE)
  MPILIB = $(MPILINK)
  CXX=@MPICXX@
  F77=@MPIF77@
  MPILIBS=$(MPILINK)
endif

我明白了

CXX was already defined in condition TRUE, which includes condition USE_MPI ...
configure.ac:12: ... `CXX' previously defined here

我没有指定编译器的规则,所以也许有办法做到这一点。

SUBDIRS = .
bin_PROGRAMS = check.cmr
check_ccmr_SOURCES = check_gen.cpp
check_ccmr_CXXFLAGS = -I$(INCLUDEDIR) $(MPIDIR)
check_ccmr_LDADD = -L$(LIBDIR)
check_ccmr_LDFLAGS = $(MPILIB)

【讨论】:

    【解决方案3】:

    如果您已将subdir-objects 选项禁用为automake,则类似的操作可能会起作用:

    配置.ac:

    AC_ARG_ENABLE([seq], ...)
    AC_ARG_ENABLE([mpi], ...)
    AM_CONDITIONAL([ENABLE_SEQ], [test $enable_seq = yes])
    AM_CONDITIONAL([ENABLE_MPI], [test $enable_mpi = yes])
    AC_CONFIG_FILES([Makefile seq/Makefile mpi/Makefile])
    

    Makefile.am:

    SUBDIRS =
    if ENABLE_SEQ
    SUBDIRS += seq
    endif
    if ENABLE_MPI
    SUBDIRS += mpi
    endif
    

    来源.am:

    ALL_SOURCES = src/foo.c src/bar.cc src/baz.cpp
    

    seq/Makefile.am:

    include $(top_srcdir)/sources.am
    
    bin_PROGRAMS = seq
    seq_SOURCES = $(ALL_SOURCES)
    

    mpi/Makefile.am:

    include $(top_srcdir)/sources.am
    
    CXX = $(MPICXX)
    AM_CPPFLAGS = -DWITH_MPI
    
    bin_PROGRAMS = mpi
    mpi_SOURCES = $(ALL_SOURCES)
    

    阻止您在同一目录中执行这两项操作的唯一方法是覆盖 $(CXX)。例如,您可以设置 mpi_CPPFLAGSautomake 会很好地处理这个问题,但是编译器开关在这里不行。

    【讨论】:

    • 无评论投反对票。做得好。我承认这有点肮脏,但你有更好的主意吗?
    【解决方案4】:

    不使用不同来源的可能解决方法是:

    myprog.seq: myprog.cxx
        $(CXX) ... myprog.cxx
    
    myprog-mpi.cxx: myprog.cxx
        @cp myprog.cxx myprog-mpi.cxx
    
    myprog.mpi: myprog-mpi.cxx
        $(MPICXX) -DWITH_MPI ... myprog-mpi.cxx
        @rm -f myprog-mpi.cxx
    

    对于 Automake:

    myprog-bin_PROGRAMS = myprog-seq myprog-mpi
    
    myprog_seq_SOURCES = myprog.c
    
    myprog-mpi.c: myprog.c
        @cp myprog.c myprog-mpi.c
    
    myprog_mpi_SOURCES = myprog-mpi.c
    myprog_mpi_LDFLAGS = $(MPI_CXXLDFLAGS)
    
    INCLUDES = $(MPI_CXXFLAGS)
    BUILT_SOURCES = myprog-mpi.c
    CLEANFILES = myprog-mpi.c
    

    【讨论】:

    • 聪明的解决方法,谢谢!我认为有一个错字:第二行 myprog..._SOURCES 应该是 myprog_mpi_SOURCES = myprog-mpi.c 然后是 mpyorog_mpi_LDFLAGS = ...
    • 您还必须将 myprog-mpi.c 列为使用 BUILT_SOURCES = myprog-mpi.c 自动生成的。
    【解决方案5】:

    这是我为构建两个静态库而提出的解决方案 - 一个带有 MPI (libmylib_mpi.a),一个没有 (libmylib.a)。这种方法的优点是不需要重复的源文件、两个变体的单个 Makefile.am 以及使用子目录的能力。您应该能够根据需要修改它以生成二进制文件而不是库。我像往常一样构建非 MPI 库,然后对于 MPI 变体,我将_SOURCES 留空并改用_LIBADD,为目标文件指定.mpi.o 的扩展名。然后我指定一个规则来使用 MPI 编译器生成 MPI 目标文件。

    整体文件/目录结构类似于

    configure.ac
    Makefile.am
    src
        mylib1.cpp
        mylib2.cpp
        ...
    include
        mylib.h
        ...
    

    配置.ac:

    AC_INIT()
    AC_PROG_RANLIB
    AC_LANG(C++)
    AC_PROG_CXX
    # test for MPI, define MPICXX, etc. variables, and define HAVE_MPI as a condition that will evaluate to true if MPI is available and false otherwise.
    AX_MPI([AM_CONDITIONAL([HAVE_MPI], [test "1" = "1"])],[AM_CONDITIONAL([HAVE_MPI], [test "1" = "2"])]) #MPI optional for xio
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
    

    可能有比我在此处列出的更有效的条件检查方法(欢迎提出建议)。

    Makefile.am:

    AUTOMAKE_OPTIONS = subdir-objects
    lib_LIBRARIES = libmylib.a
    libmylib_a_SOURCES = src/mylib_1.cpp src/mylib_2.cpp ...
    
    #conditionally generate libmylib_mpi.a if MPI is available
    if HAVE_MPI
        lib_LIBRARIES += libmylib_mpi.a
        libmylib_mpi_a_SOURCES = #no sources listed here
        #use LIBADD to specify objects to add - use the basic filename with a .mpi.o extension
        libmylib_mpi_a_LIBADD = src/mylib_1.mpi.o src/mylib_2.mpi.o ...
    endif
    AM_CPPFLAGS = -I${srcdir}/include
    
    include_HEADERS = include/mylib.h
    
    # define a rule to compile the .mpi.o objects from the .cpp files with the same name
    src/%.mpi.o: ${srcdir}/src/%.cpp ${srcdir}/include/mylib.h
        $(MPICXX)  $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -DWITH_MPI=1 -c $(patsubst %.mpi.o,$(srcdir)/%.cpp,$@) -o $@
    
    #define a rule to clean the .mpi.o files
    clean-local:
        -rm -f src/*.mpi.o
    

    【讨论】:

      【解决方案6】:

      MPI 安装确实(通常)附带编译器包装器,但不要求您使用它们——MPI 确实坚持它。如果您想走自己的路,您可以编写自己的 makefile 以确保 C++ 编译器获得正确的库(等)。要找出正确的库(等)是什么,请检查编译器包装器,在我使用的所有系统上,它是一个 shell 脚本。

      乍一看,英特尔编译器等产品附带的编译器包装器有点令人生畏,但停下来想想发生了什么——您只是在编译一个使用一两个外部库的程序。编写一个使用 MPI 库的 makefile 并不比编写一个使用任何其他库的 makefile 困难。

      【讨论】:

      • 感谢您的回答。尽管我不需要使用 MPI 编译器包装器,但是那里有许多 MPI 实现,并且每个都使用自己的库名称等——我宁愿重写 Makefile 节以使用 $(MPICXX) 而不是维护几行 automake/autoconf 代码为每个 MPI 版本提供 CPPFLAGS/LDFLAGS/LIBS...
      • 如果您只关心一个平台,那就不再困难了。但是,我在 3 或 4 个不同的集群架构上运行,虽然它们都共享一个 mpicc 编译器,但不同平台的特定标志和库是不同的。除非您在脚本,这是我建议做的。看我的回答。
      猜你喜欢
      • 2012-05-09
      • 1970-01-01
      • 2012-03-07
      • 1970-01-01
      • 2014-06-03
      • 2015-11-08
      • 2012-07-03
      • 2021-11-23
      • 1970-01-01
      相关资源
      最近更新 更多