Код MPI значительно медленнее при компиляции с флагом -fopenmp (для MPI с многопоточностью) - PullRequest
0 голосов
/ 05 октября 2019

Я компилирую код Fortran 90 с помощью компилятора mpif90 с двумя разными make-файлами, первый из которых выглядит так:

FC = mpif90
FFLAGS = -Wall -ffree-line-length-none 
FOPT = -O3

all: ParP2S.o ParP2S
ParP2S.o: ParP2S.f90
        $(FC) $(FFLAGS) $(FOPT) ParP2S.f90 -c
ParP2S: ParP2S.o
        $(FC) $(FFLAGS) $(FOPT) ParP2S.o -o ParP2S
clean: 
        rm -f *.o* rm -f *.o* 

второй make-файл выглядит очень похоже, я просто добавил флаг -fopenmp;

FC = mpif90
FFLAGS = -Wall -ffree-line-length-none -fopenmp
FOPT = -O3

all: ParP2S.o ParP2S
ParP2S.o: ParP2S.f90
        $(FC) $(FFLAGS) $(FOPT) ParP2S.f90 -c
ParP2S: ParP2S.o
        $(FC) $(FFLAGS) $(FOPT) ParP2S.o -o ParP2S
clean: 
        rm -f *.o* rm -f *.o* 

Второй make-файл предназначен для гибридной (MPI с OpenMP) версии кода. На данный момент у меня точно такой же код, но скомпилированный с этими разными make-файлами. Во втором случае код работает более чем в 100 раз медленнее. Любые комментарии в том, что я делаю неправильно?

edit 1: Я не запускаю многопоточные задачи. На самом деле, код не имеет никаких директив OpenMP, это просто чистый MPI-код, но скомпилированный с другим make-файлом. Тем не менее, я попытался выполнить следующие команды (см. Ниже), и это не помогло.

export MV2_ENABLE_AFFINITY=0
export OMP_NUM_THREADS=1
export OMP_PROC_BIND=true
mpirun -np 2 ./ParP2S

edit 2: Я использую gcc версии 4.9.2 (я знаю, что в старой версии была ошибка с векторизацией с fopenmp). Я думал, что включение флага -fopenmp может помешать оптимизации компилятора, однако, после прочтения интересного обсуждения ( Может ли оптимизация компилятора быть заблокирована многопоточностью? ) Я не уверен, так ли это,Кроме того, так как мой код не имеет никаких директив OpenMP, я не понимаю, почему код, скомпилированный с -fopenmp, должен быть настолько медленным.

edit3: Когда я запускаю без -fopenmp (первый make-файл), это занимает около0,2 секунды без оптимизаций (-O0) и 0,08 секунды с оптимизациями (-O3), но с флагом -fopenmp это занимает около 11 секунд с -O3 или -O0.

1 Ответ

0 голосов
/ 06 октября 2019

Оказалось, что проблема была действительно в задании, как это было предложено Владимиром Ф. и Жилем Гуаиллардетом (большое спасибо!).

Сначала я понял, что использую MPI с OpenMPI версии 1.6.4, а не MVAPICH2, поэтому команда export MV2_ENABLE_AFFINITY=0 не имеет здесь никакого реального значения. Во-вторых, я (предположительно) позаботился о сходстве различных потоков OpenMP, установив

export OMP_PROC_BIND=true
export OMP_PLACES=cores

, но не устанавливал правильные привязки для процессов MPI, так как неправильно запускал приложение как

mpirun -np 2 ./Par2S

и, похоже, что с OpenMPI версии 1.6.4, более подходящий способ сделать это -

mpirun -np 2 -bind-to-core -bycore -cpus-per-proc 2  ./hParP2S

Команда -bind-to-core -bycore -cpus-per-proc 2 обеспечивает 2 ядра для моего приложения (см. https://www.open -mpi.org / doc / v1.6 / man1 / mpirun.1.php , а также комментарии Жиля Гуаиллардета к Убедитесь, что гибридный MPI / OpenMP запускает каждый поток OpenMP на отдельном ядре ). Без этого оба MPI-процесса собирались в одно ядро, что стало причиной низкой эффективности кода, когда в Makefile использовался флаг -fopenmp.

Очевидно, что при запуске чистого MPI-кода, скомпилированного без флага -fopenmp, различные MPI-процессы автоматически переходят на разные ядра, но при -fopenmp необходимо указывать привязки вручную, как описано выше.

В целях полноты я должен упомянуть, что не существует стандарта для установки правильного соответствия задач, поэтому мое решение не будет работать, например, с MVAPICH2 или (возможно) с различными версиями OpenMPI. Кроме того, для выполнения nproc процессов MPI с nthreads каждый в ncores ЦП потребуется, например,

export OMP_PROC_BIND=true
export OMP_PLACES=cores
export OMP_NUM_THREADS=nthreads

mpirun -np nproc -bind-to-core -bycore -cpus-per-proc ncores ./hParP2S

, где ncores = nproc * nthreads.

пс: мой код имеет MPI_all_to_all. Условие, когда более одного процесса MPI находятся на одном ядре (без гиперпоточности), вызывающего эту подпрограмму, должно быть причиной того, что код был примерно в 100 раз медленнее.

...