如何使用 automake 编译同一程序的 MPI 和非 MPI 版本?
Posted
技术标签:
【中文标题】如何使用 automake 编译同一程序的 MPI 和非 MPI 版本?【英文标题】:how to compile MPI and non-MPI version of the same program with automake? 【发布时间】: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 编译,您将不得不这样做。 【参考方案1】:我有同样的问题,我发现没有真正好的方法让自动工具有条件地为特定目标使用 MPI 编译器。 Autotools 擅长根据您的源代码编写的语言(CC
、CXX
、FC
、F77
等)确定要使用的编译器,但它确实不擅长弄清楚是否对特定目标使用 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
以查看宏是否找到它。
我已经使用mvapich 和OpenMPI 测试了这个宏,以及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_CPPFLAGS
和 automake
会很好地处理这个问题,但是编译器开关在这里不行。
【讨论】:
无评论投反对票。做得好。我承认这有点肮脏,但你有更好的主意吗?【参考方案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 编译器,但不同平台的特定标志和库是不同的。除非您在脚本,这是我建议做的。看我的回答。以上是关于如何使用 automake 编译同一程序的 MPI 和非 MPI 版本?的主要内容,如果未能解决你的问题,请参考以下文章
水文日常~~Linux命令行下如何编译运行MPI程序(C/C++)
水文日常~~Linux命令行下如何编译运行MPI程序(C/C++)
水文日常~~Linux命令行下如何编译运行MPI程序(C/C++)