как скомпилировать MPI и не-MPI версию одной и той же программы с automake?

У меня есть код C++, который можно скомпилировать с поддержкой MPI в зависимости от определенного флага препроцессора; отсутствует соответствующий флаг, исходники компилируются в непараллельную версию.

Я хотел бы настроить Makefile.am так, чтобы он компилировал как MPI-параллельную и последовательную версию, если указана опция ./configure.

Вот в чем загвоздка: MPI имеет собственную оболочку компилятора C++ и настаивает на том, чтобы исходные коды компилировались и компоновались с ее помощью, а не со стандартным компилятором C++. Если бы я сам написал Makefile, мне пришлось бы сделать что-то вроде этого:

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

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

Есть ли способ сообщить automake, что он должен использовать $(MPICXX) вместо $(CXX) при компиляции версии программы с поддержкой MPI?


person Riccardo Murri    schedule 19.10.2010    source источник
comment
Вас волнует, связана ли последовательная программа с библиотеками MPI в обоих случаях сборки? Если нет, вы можете просто использовать mpicxx для обоих — это ничего не повредит. И если вы не собираете версию mpi, вы можете использовать g++ для всего. Эмпирически это похоже на то, что делают многие пакеты, которые имеют как параллельные, так и последовательные версии - на ум приходит hdf5.   -  person Jonathan Dursi    schedule 19.10.2010
comment
В общем-то нет, и я раньше так компилировал. Однако я разрабатываю некоторые инструменты PMPI, и, как правило, вы не хотите связывать библиотеку-перехватчик с MPI, что вы вынуждены делать, если компилируете с помощью mpicc.   -  person Todd Gamblin    schedule 20.10.2010


Ответы (6)


У меня та же проблема, и я обнаружил, что нет действительно хорошего способа заставить автоинструменты условно использовать компиляторы 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 также устанавливает некоторые переменные оболочки, чтобы вы могли проверить в своем файле configure.ac, был ли найден MPI. например, если вы ищете поддержку MPI C++, вы можете проверить $have_CXX_mpi, чтобы увидеть, нашел ли ее макрос.

Я протестировал этот макрос с помощью mvapich и OpenMPI, а также пользовательскую реализацию MPICH2 на BlueGene (хотя это не решает всех проблем кросс-компиляции, которые вы там увидите). Дайте мне знать, если что-то не работает. Я хотел бы сохранить макрос как можно более надежным.

person Todd Gamblin    schedule 19.10.2010
comment
Вы должны добавить его в LDADD вместо LDFLAGS, иначе какой-нибудь компоновщик выдаст ошибку. И в самом сценарии есть ошибка: с sed для удаления завершающего пробела вы должны использовать sed/ +/ /g вместо sed/ */ /g. - person w00d; 23.06.2011
comment
Ваш макрос у меня работает нормально. Однако мне нужно запустить его дважды, чтобы получить значения как из MPICC, так и из MPICXX. Во втором вызове я заключаю макрос между AC_LANG_PUSH([C++]) и AC_LANG_POP([C++]). Спасибо за СВ. - person user9869932; 03.11.2015

Мне жаль, что автоматизация с использованием MPI настолько сложна. Я боролся с этим в течение многих месяцев, пытаясь найти хорошее решение. У меня есть исходное дерево, в котором есть одна библиотека, а затем множество программ в подпапках, которые используют эту библиотеку. Некоторые из папок являются программами mpi, но когда я пытаюсь заменить CXX компилятором MPI, используя in Makefile.am.

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)
person fsteveb    schedule 26.07.2012

Если вы отключили параметр 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

источники.ам:

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

последовательность/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 справились бы с этим изящно, но переключатель компилятора делает его здесь недопустимым.

person Jack Kelly    schedule 19.10.2010
comment
Понизить без комментариев. Прекрасная работа. Я допускаю, что это немного грязный хак, но у вас есть идея получше? - person Jack Kelly; 23.10.2010

Возможный обходной путь для отказа от использования разных источников может быть следующим:

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
person Treviño    schedule 14.01.2011
comment
Умный обходной путь, спасибо! Я думаю, что это опечатка: вторая строка myprog..._SOURCES должна читаться как myprog_mpi_SOURCES = myprog-mpi.c, а затем mpyorog_mpi_LDFLAGS = ... - person Riccardo Murri; 18.01.2011
comment
Вы также должны указать myprog-mpi.c как автоматически сгенерированный с использованием BUILT_SOURCES = myprog-mpi.c. - person user562374; 25.01.2011

Вот решение, которое я придумал для создания двух статических библиотек — одной с 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
person Brian W. Mulligan    schedule 15.10.2017

Установки MPI (обычно) поставляются с оболочками компилятора, но нет никаких требований, чтобы вы их использовали - MPI не настаивает на этом. Если вы хотите пойти своим путем, вы можете написать свой собственный make-файл, чтобы гарантировать, что компилятор C++ получит правильные библиотеки (и т. д.). Чтобы выяснить, что такое правильные библиотеки (и т. д.), проверьте оболочку компилятора, которая на всех системах, которые я использовал, является сценарием оболочки.

На первый взгляд оболочки компилятора, которые поставляются с такими продуктами, как компиляторы Intel, немного пугают, но остановитесь и подумайте о том, что происходит — вы просто компилируете программу, которая использует внешнюю библиотеку или две. Написать make-файл для использования библиотек MPI не сложнее, чем написать make-файл для использования любой другой библиотеки.

person High Performance Mark    schedule 19.10.2010
comment
Спасибо за Ваш ответ. Хотя я не обязан использовать компилятор-оболочку MPI, существует много реализаций MPI, и каждая из них использует свои собственные имена библиотек и т. д. -- Я бы предпочел переписать строфу Makefile, чтобы использовать $(MPICXX), чем поддерживать несколько строк кода automake/autoconf для предоставления CPPFLAGS/LDFLAGS/LIBS для каждой версии MPI... - person Riccardo Murri; 19.10.2010
comment
Это не сложнее, если вам нужна одна платформа. Тем не менее, я работаю на 3 или 4 разных кластерных архитектурах, и хотя все они используют компилятор mpicc, конкретные флаги и библиотеки для разных платформ, ну, разные. script, что я и рекомендую сделать. Смотрите мой ответ. - person Todd Gamblin; 20.10.2010