почему mpi_bcast намного медленнее, чем mpi_reduce?

Используя MPI, мы можем выполнить широковещательную передачу для отправки массива множеству узлов или уменьшить объединение массивов из множества узлов в один узел.

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

Кажется, нет никаких причин, по которым вещание будет особенно медленным, чем сокращение?

Я запустил следующую тестовую программу на кластере из 4 компьютеров, где каждый компьютер имеет 12 ядер. Странно то, что трансляция была намного медленнее, чем сокращение. Зачем? Что я могу с этим поделать?

Результаты были:

inited mpi: 0.472943 seconds
N: 200000 1.52588MB
P = 48
did alloc: 0.000147641 seconds
bcast: 0.349956 seconds
reduce: 0.0478526 seconds
bcast: 0.369131 seconds
reduce: 0.0472673 seconds
bcast: 0.516606 seconds
reduce: 0.0448555 seconds

Код был:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <sys/time.h>
using namespace std;

#include <mpi.h>

class NanoTimer {
public:
struct timespec start;

NanoTimer() {
clock_gettime(CLOCK_MONOTONIC,  &start);

}
double elapsedSeconds() {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC,  &now);
double time = (now.tv_sec - start.tv_sec) + (double) (now.tv_nsec - start.tv_nsec) * 1e-9;
start = now;
return time;
}
void toc(string label) {
double elapsed = elapsedSeconds();
cout << label << ": " << elapsed << " seconds" << endl;
}
};

int main( int argc, char *argv[] ) {
if( argc < 2 ) {
cout << "Usage: " << argv[0] << " [N]" << endl;
return -1;
}
int N = atoi( argv[1] );

NanoTimer timer;

MPI_Init( &argc, &argv );
int p, P;
MPI_Comm_rank( MPI_COMM_WORLD, &p );
MPI_Comm_size( MPI_COMM_WORLD, &P );
MPI_Barrier(MPI_COMM_WORLD);
if( p == 0 ) timer.toc("inited mpi");
if( p == 0 ) {
cout << "N: " << N << " " << (N*sizeof(double)/1024.0/1024) << "MB" << endl;
cout << "P = " << P << endl;
}
double *src = new double[N];
double *dst = new double[N];
MPI_Barrier(MPI_COMM_WORLD);
if( p == 0 ) timer.toc("did alloc");

for( int it = 0; it < 3; it++ ) {
MPI_Bcast( src, N, MPI_DOUBLE, 0, MPI_COMM_WORLD );
MPI_Barrier(MPI_COMM_WORLD);
if( p == 0 ) timer.toc("bcast");

MPI_Reduce( src, dst, N, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD );
MPI_Barrier(MPI_COMM_WORLD);
if( p == 0 ) timer.toc("reduce");
}

delete[] src;

MPI_Finalize();
return 0;
}

Узлы кластера работали под управлением 64-разрядной версии Ubuntu 12.04. Я пробовал и openmpi, и mpich2, и получил очень похожие результаты. Сеть — это гигабитная сеть Ethernet, которая не самая быстрая, но меня больше всего интересует не абсолютная скорость, а несоответствие между широковещательной передачей и сокращением.

1

Решение

Я не думаю, что это вполне отвечает на ваш вопрос, но я надеюсь, что это даст некоторое понимание.

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

В любом случае, вы должны рассмотреть, что делает каждая из этих функций. Вещание берет информацию из одного процесса и передает ее всем другим процессам; Снижение берет информацию от каждого процесса и сводит ее к одному процессу. Согласно (самый последний) стандарт, MPI_Bcast считается коллективной операцией «один ко всем», а MPI_Reduce считается коллективной операцией «все к одному». Поэтому ваша интуиция об использовании двоичных деревьев для MPI_Reduce, вероятно, найдена в обеих реализациях. Однако, скорее всего, он не найден в MPI_Bcast. Это может быть случай, когда MPI_Bcast реализован с использованием неблокирующей двухточечной связи (отправка от процесса, содержащего информацию, ко всем другим процессам) с ожиданием всего после связи. В любом случае, чтобы выяснить, как работают обе функции, я бы предложил углубиться в исходный код ваших реализаций OpenMPI и MPICH2.

2

Другие решения

Как упоминал Христо, это зависит от размера вашего буфера. Если вы отправляете большой буфер, широковещательная передача должна будет выполнить много больших отправок, в то время как прием выполняет некоторую локальную операцию над буфером, чтобы уменьшить его до одного значения, а затем передает только это одно значение вместо полного буфера ,

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector