С++ MPI, используя несколько узлов, сначала уменьшите на уровне узла, затем уменьшите до головного узла

Я использую кластер Windows HPC с 12 узлами (каждый с 24 ядрами) для запуска программы C++ MPI (используйте Boost MPI). Один запуск с уменьшением MPI, одно уменьшение MPI с комментарием (только для теста скорости). Время выполнения 01:17:23 и 01:03:49. Мне кажется, что сокращение MPI занимает большую часть времени. Я думаю, что стоит попробовать сначала уменьшить на уровне узла, а затем уменьшить до головного узла, чтобы повысить производительность.

Ниже приведен простой пример для тестирования. Предположим, есть 4 компьютерных узла, каждый из которых имеет 2 ядра. Я хочу сначала использовать mpi для уменьшения на каждом узле. После этого сведите к головному узлу. Я не совсем знаком с mpi, и приведенная ниже программа дает сбой.

#include <iostream>
#include <boost/mpi.hpp>
namespace mpi = boost::mpi;
using namespace std;

int main()
{
  mpi::environment env;
  mpi::communicator world;

  int i = world.rank();


  boost::mpi::communicator local = world.split(world.rank()/2); // total 8 cores, divide in 4 groups
  boost::mpi::communicator heads = world.split(world.rank()%4);

  int res = 0;

  boost::mpi::reduce(local, i, res, std::plus<int>(), 0);
  if(world.rank()%2==0)
  cout<<res<<endl;
  boost::mpi::reduce(heads, res, res, std::plus<int>(), 0);

  if(world.rank()==0)
      cout<<res<<endl;

  return 0;
}

Вывод неразборчив, что-то вроде этого

Z
h
h
h
h
a
a
a
a
n
n
n
n
g
g
g
g
\
\
\
\
b
b
b
b
o
o
o
o
o
o
o
o
s
...
...
...

Сообщение об ошибке

Test.exe ended prematurely and may have crashed. exit code 3

Я подозреваю, что сделал что-то не так с разделением/или сокращением группы, но не могу понять это с помощью нескольких испытаний. Как мне изменить, чтобы это работало? Спасибо.


person user11594134    schedule 01.07.2019    source источник
comment
Почему? По какой причине? Кроме того, всегда предоставляйте минимальный воспроизводимый пример и более четкое описание проблемы.   -  person Zulan    schedule 02.07.2019
comment
вы хотите заново изобрести MPI_Reduce() или MPI_Allreduce() ? Вообще говоря, вы должны позволить библиотеке MPI оптимизировать это для вас (иерархический алгоритм не всегда самый эффективный алгоритм). В MPI вы бы использовали разные буферы отправки и приема или MPI_IN_PLACE, хотя не уверен, что это применимо к Boost.MPI.   -  person Gilles Gouaillardet    schedule 02.07.2019
comment
@Zulan, как упоминалось в отредактированной версии, это всего лишь пример. В моей реальной программе я запускаю ее на кластере из 12 узлов (каждый с 24 ядрами). Фаза сокращения медленная. Это причина. Приведенный выше код будет скомпилирован, но вылетит, если у вас есть boost mpi, мне нужно выяснить, почему. Для простого кода MPI я не знаю, как его написать.   -  person user11594134    schedule 02.07.2019
comment
@GillesGouaillardet, это отличный момент. MPI_Reduce может уже выполнить эту оптимизацию. Это может оказаться еще медленнее, я думаю, что все еще хочу попробовать, чтобы убедиться.   -  person user11594134    schedule 02.07.2019
comment
«уменьшить очень медленно» может означать, что существует некоторый дисбаланс, и корневая задача должна (неявно) ждать других. Вы должны добавить барьеры и таймеры, чтобы выяснить, тратится ли время на барьеры (например, дисбаланс) или на общение (например, медленное сокращение).   -  person Gilles Gouaillardet    schedule 02.07.2019
comment
@GillesGouaillardet, ты совершенно прав. В моей программе, безусловно, есть дисбаланс, и это большая проблема. Я не нашел способа решить эту проблему и должен отложить это. Логика, стоящая за этой идеей двухэтапного сокращения, такова: часть сокращения в реальной программе нетривиальна (сложный класс, много сложений, делений), это может сэкономить время узлу, который уже завершил все операции. задача сделать уменьшить. Когда наступит окончательный редуктор, голове просто нужно уменьшить 1 для этого узла вместо 24. Как вы предположили, это может оказаться еще хуже. Просто хочу убедиться.   -  person user11594134    schedule 02.07.2019
comment
Попытка оптимизировать проблему с производительностью, чье узкое место не полностью понимается, как правило, плохая идея. Я бы посоветовал сначала использовать подходящий инструмент анализа производительности с поддержкой MPI. Кроме того, фрагмент кода не является минимально воспроизводимым примером - он не компилируется и вы всегда должны представлять свои собственные попытки отладки. Также попробуйте объяснить аргументацию вашего кода? Какие ранги участвуют в двухэтапном сокращении?   -  person Zulan    schedule 02.07.2019
comment
@Zulan по вашему предложению вопрос был отредактирован. Я не совсем знаком с инструментом анализа производительности MPI. У вас есть какие-нибудь рекомендации? Спасибо   -  person user11594134    schedule 02.07.2019
comment
Я понятия не имею об окнах, но поисковая система даст вам прекрасные указания относительно инструментов анализа производительности MPI. Если вы работаете на сайте HPC, спросите у оператора. Спасибо за обновление вопроса, но обратите внимание, что код по-прежнему не компилируется (если вам все еще интересно, почему код дает сбой).   -  person Zulan    schedule 02.07.2019
comment
@Zulan, у тебя установлены MPI и boost mpi? Если да, то это не должно быть проблемой. Какая у вас ошибка компиляции?   -  person user11594134    schedule 02.07.2019
comment
Заголовки и основная функция отсутствуют. Любой компилятор должен сказать вам, пытались ли вы сказать фактический код из этого вопроса.   -  person Zulan    schedule 03.07.2019
comment
@Zulan, весь код опубликован. Давайте посмотрим, есть ли у вас все еще ошибки компиляции.   -  person user11594134    schedule 03.07.2019


Ответы (1)


Причина наличных в том, что вы дважды передаете одну и ту же переменную в MPI в следующей строке.

boost::mpi::reduce(heads, res, res, std::plus<int>(), 0);

Это не совсем хорошо задокументировано в Boost.MPI, но boost берет их по ссылке и передает соответствующие указатели в MPI. MPI вообще запрещает дважды передавать один и тот же буфер одному и тому же вызову. Чтобы быть точным, выходной буфер, переданный в функцию MPI, не должен быть псевдонимом (перекрываться) с любым другим буфером, переданным в этом вызове.

Вы можете легко исправить это, создав копию res.

Я также думаю, что вы, вероятно, захотите ограничить вызов второго сокращения из процессов с local.rank() == 0.

Также повторяю комментарий - я сомневаюсь, что вы получите какую-либо выгоду от повторной реализации сокращения. Попытка оптимизировать проблему с производительностью, чье узкое место не полностью понимается, как правило, плохая идея.

person Zulan    schedule 03.07.2019
comment
это решает проблему. Я действительно многому учусь на этом. Большое спасибо - person user11594134; 03.07.2019