LINUX.ORG.RU

from openMP to MPI

 , , , ,


0

1

пытаюсь перейти на MPI с openMP... чтобы использовать на кластере...
читаю о этом MPI и ничего не понимаю...
может кто посоветует где почитать в доступтой форме!
все что гуглил, не осилил...
есть вот такое:

#include <omp.h>
...
int main() {

LaserChar *laserChar = new LaserChar;

readFile(*laserChar);

#pragma omp parallel for collapse(2)
    for (size_t i=0; i<laserChar->V1.size(); ++i) {       
        for (size_t j=0; j<laserChar->V2.size(); ++j) {
                Design *des=new Design;
                des->value=laserChar->parameters[i][j];
                some_function(*des);
                laserChar->value[i][j]=des->results;
                delete des;
}}

writeFile(*laserChar);
delete laserChar;
return 0;
}

как его переписать в MPI? или что я делаю не так (желательно не в грубой форме)?
p.s. этот говнокод работает, а это главное.


laserChar->V2.size()

А эта функция же в каждом цикле будет вызываться? Жестоко. Или так надо?

anonymous
()
Ответ на: комментарий от anonymous

там sizes максимум 100.
где size большой... использую:
const size_t V_size=V.size();

вот мне эти 100x100 нужной разбросать не по ядрам а по нодам и их ядрам...

rgB
() автор топика
Ответ на: комментарий от Deleted

я читал то что-то на opennet.ru... стаковерфлоу... встречал лабы по MPI... не осилил.
это будет звучать глупо...
но мне все методы не нужны... мне нужно както отправить структуру на другую ноду... там что бы посчитало ёё и вернуло назад.
если ткнете носом где этo рассказанно, буду благодарен.
p.s. понимаю что нужен не нулевой уровень... но он у меня такой...

rgB
() автор топика
Ответ на: комментарий от slovazap

Послушал. Говорят что в С++ методы класса с квалификатором const не могут менять данные объекта. Но вызываться-то они вызываются и выполняются каждый раз. Так?

anonymous
()
Ответ на: комментарий от rgB

Может будет полезно

Не программист. Расскажу точку зрения пользователя. Openmp при расчетах используется общая память к которой обрщаются потоки в которых производятся вычисления.

При mpi запускаются (как я понял) независимые процессы, взаимодействие между которыми происходит через mpirun.

Мне кажется, что в лоб одно на другое не поменять. Так, например, при расчёте развития пожара в fds при openmp можно было сделать одну большую расчетную сетку со всем «горящим» зданием, которая параллелилась автоматически на ядра процессора.

При mpi на одном узле можно было рассчитывать только одну расчетную сетку. Для раскидывания по нодам требовалось применять несколько расчетных сеток для одного объекта. В итоге, каждая сетка автоматически уходила на свободную ноду, между которыми при расчёте передавались граничные значения на гранях расчетных сеток.

Jurik_Phys ★★★★★
()
Ответ на: Может будет полезно от Jurik_Phys

спасибо!
саму идею распараллеливания я так и представляю...
у меня эти 100x100 между собой независимо...
можно же создать 100 объектов(mpi) по 100 потоков (openmp)...
или идеально: n(количество нод) по 1e4/n потоков... как то так
важно что бы все 6 нод пo 12 ядер постояно были загруженны...
а не 12 ядер только...

rgB
() автор топика
Ответ на: комментарий от anonymous

Нет, они заинлайнятся в константу даже на -O0, поэтому это уродство с выносом const size_t никогда не нужно. Более того, оно вредно, потому что если вы начнёте менять контейнер в теле цикла, код с ручным const size_t сломается, а в случае size() в цикле компилятор просто подставит обращение к члену класса.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 3)

openMP рассчитан на систему с общей памятью между процессорами

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

так что придётся всё же что-то, да осилить

ах да, ноды же могут отказывать, так что нужно чекпоинты делать

dimon555 ★★★★★
()
Ответ на: комментарий от dimon555

ах да, ноды же могут отказывать, так что нужно чекпоинты делать...

вот это огорчает...

MPI, там у каждой ноды своя память ... и это где-то собиралось

мне ноде передать {struc St,double X,double Y},
после some_function() она там запишет в /user/user_name/file_X_Y.txt
вернет vector<double> {a,b,c,d} в мастер...
на основной ноде пишем file.txt с этих vector<double> {a[],b[],c[],d[]}
вот такая идея...

rgB
() автор топика
Ответ на: комментарий от dimon555

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

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

Sahas ★★★★☆
()

тут уже про MPI разъясняли, но попробую в популярной форме ещё раз изложить.

Параллелилизация при помощи MPI подразумевает запуск N независимых процессов (через команду mpirun -n N ./your_program). Это полностью независимые экземпляры одной и той же программы, со своей памятью и данными. У каждого процесса есть rank (от 0 до N-1), процессы могут общаться друг с другом через MPI сообщения. Если надо, чтобы каждый процесс выполнил свою задачу, в коде пишется что-то типа:

if (rank==0) {
  do something for rank 0...
}
else if (rank==1)  {
  do something for rank 1...
}
...

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

Твоя задача («мне нужно както отправить структуру на другую ноду... там что бы посчитало ёё и вернуло назад.») не очень ясна, это больше похоже на просто удалённый запуск программы. Поясни, пожалуйста, что конкретно и зачем тебе нужно.

Sahas ★★★★☆
()
Последнее исправление: Sahas (всего исправлений: 1)
Ответ на: комментарий от anonymous

Наверняка будет зверски заоптимизированно компилятором.

hotpil ★★★★
()
Ответ на: комментарий от Sahas

есть лазер с некоторыми исходными характеристиками: laserChar [v1, v2, v3, T, V].

struct laserChar 
{vector<double> v1, v2, v3, T, V; 
vector<vector<double>> P, I};

и нужно для каждого Т[i], V[j] for T.size() and V.size() найти значения P[i][j], I[i][j].
получается нужно запустить(по очереди, а не сразу все) 100x100 независимых исчислений функции:
some_function(v1, v2, v3, T[i], V[j]);
которая возвращает вектор 2x чисел {P[i][j], I[i][j]}, их возвращаем нашей структуре laserChar и пишем их в файл.

вот с помощью openMP реализовано и показано выше.

rgB
() автор топика
Ответ на: комментарий от slovazap

они заинлайнятся в константу даже на -O0

Правда чтоли?

#include <iostream>
#include <vector>

int main (int argc, char *argv[])
{
   int n;
   std::cin >> n;
   std::vector<int> v(n);
   for (std::size_t i = 0; i < v.size(); i++)
	std::cin >> v[i];
   return 0;
}
.L3:
	leaq	-64(%rbp), %rax
	movq	%rax, %rdi
	call	_ZNKSt6vectorIiSaIiEE4sizeEv
	cmpq	-24(%rbp), %rax
	seta	%al
	testb	%al, %al
	je	.L2
	movq	-24(%rbp), %rdx
	leaq	-64(%rbp), %rax
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	_ZNSt6vectorIiSaIiEEixEm
	movq	%rax, %rsi
	movl	$_ZSt3cin, %edi
.LEHB2:
	call	_ZNSirsERi
.LEHE2:
	addq	$1, -24(%rbp)
	jmp	.L3
.L2:
	movl	$0, %ebx
	leaq	-64(%rbp), %rax
	movq	%rax, %rdi
yoghurt ★★★★★
()
Ответ на: комментарий от Sahas

в openMP это распараллеливание реализовано прекрасно.
N потоков, загружаются сразу.
только один освободился, запускается новая задача some_function().

rgB
() автор топика
Ответ на: комментарий от yoghurt

Заинлайниться-то заинлайнится, но константность метода-то тут причем?

При том что заинлайниться в константу оно имеет возможность только если у объекта в цикле не вызывается не-константных методов. Хотя если реализации методов доступны в compile time, наверное для современных оптимизаторов даже это не обязательно.

Правда чтоли?

Конечно.

.LBB0_1:                                # %for.cond
                                        # =>This Inner Loop Header: Depth=1
        leaq    -72(%rbp), %rax
        movq    -80(%rbp), %rcx
        movq    %rax, -24(%rbp)
        movq    -24(%rbp), %rax
        movq    8(%rax), %rdx
        movq    (%rax), %rax
        subq    %rax, %rdx
        sarq    $2, %rdx
        cmpq    %rdx, %rcx
        jae     .LBB0_7
# BB#2:                                 # %for.body
slovazap ★★★★★
()
Ответ на: комментарий от rgB

При использовании MPI каждый процесс может обрабатывать свой кусок laserChar и писать параллельно или последовательно в один файл

Sahas ★★★★☆
()
Ответ на: комментарий от Sahas

задача подразумевает параллелизацию на почти независимые куски

как бы да...
+ тогда сама функция some_function() сможет спокойно прагмой параллелиться на 12 ядер...
и не будет большой конкуренции между потоками.

rgB
() автор топика
Ответ на: комментарий от Sahas

то есть писать в общий array или вектор оно не сможет?...
тогда проще создать N файлов с 4-я числами (что бы небыло сбоев).
считать их в один. и поудалять. но это не красиво как то. такое я делал пару лет назад...

rgB
() автор топика
Ответ на: комментарий от Sahas

ок. буду что то мутить классическим методом тыка..

rgB
() автор топика
Ответ на: комментарий от rgB

то есть писать в общий array или вектор оно не сможет?...

Нет, это MPI :) Но можно вызвать вызвать функцию (кажись, mpi_bcast), которая ссинхронизирует массив laserChar. А потом писать в файл можно уже только одним процессом (например, с rank=0). Или все процессы буду посылать свои части laserChar нулевому. Но это всё подразумевает «ручное» управление пересылкой данных...

Sahas ★★★★☆
()
Ответ на: комментарий от Sahas

к сожалению...
вот этот бред не прокатил даже на компe:

#include <stdio.h>
#include "mpi.h"
#include <omp.h>
#include "math.h"
#include "vector"

double MasterProcess(std::vector <double> v1, std::vector <double> v2, int i) {double X=v1[i]+v2[i]+i;
 return X;
}

struct LaserChar {
std::vector <double> v1, v2, v3;
};

int main(int argc, char *argv[]) {

LaserChar laserChar;
  for (size_t i=0; i<5; ++i) { laserChar.v1.push_back(i); laserChar.v2.push_back(i);
  }

int numprocesses, rank, namelen;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &numprocesses);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#pragma omp parallel for
 for (size_t i=0; i<3; ++i) {
     laserChar.v3.push_back (MasterProcess(laserChar.v1, laserChar.v2 , rank)); }

  MPI_Finalize();

  std::cout << laserChar.v3.size() << std::endl;
  for (size_t i=0; i<laserChar.v3.size(); ++i) 
    std::cout << laserChar.v3[i] << "\t";
    std::cout << std::endl;
}
> mpic++ intr.cpp -fopenmp -O3 -o intr && mpirun -np 3 intr
3
6       6       6
3
3       3       3
3
0       0       0
спасибо за разъяснения и советы!
придется разбираться на досуге и вернуться к этому вопросу познее...

rgB
() автор топика
Ответ на: комментарий от rgB

Программа отработала правильно. Каждый mpi-процесс пробежался по всему (своему) массиву и заполнил его в соответствии с переданным значением i, которое равно rank.

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

Sahas ★★★★☆
()
Ответ на: комментарий от Sahas

Программа отработала правильно.

это я уже понял после запуска.
была надежда, что он просто его переписывать будет О_о
+ получается... на каждой ноде создаться своя структура ...

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

мне кажется... проще будет писать 100x100 файлов... потом их собирать... и другой прогой собирать инфу в один...
большая надежда, что на любой ноде /user/user_name это линк на основную ноду, и все будет находиться там.

rgB
() автор топика
Ответ на: комментарий от slovazap

заинлайниться в константу оно имеет возможность только если у объекта в цикле не вызывается не-константных методов

Звучит разумно, при условии, что const-метод всегда возвращает одинаковый результат. То есть, как бы, возвращаемое значение такого метода тоже «относится» к данным объекта. Но тогда получается, что такая конструкция невозможна:

int Foo::const_method() const
{
   return rand();
}

Может ли const-метод вызывать не-const методы или функции? В гугле не нашёл. А компилятор устанавливать не хочется.

anonymous
()
Ответ на: комментарий от rgB

странная у тебя задача... С таким же успехом можно запустить 100х100 копий программы с разными аргументами командной строки (что-то типа for I in $(seq 1 100); do for J in $(seq 1 100); do ./intr $I $J &; done; done). И не надо с MPI мучиться :)

Sahas ★★★★☆
()
Последнее исправление: Sahas (всего исправлений: 1)
Ответ на: комментарий от Sahas

это в лоб(bash)... а хочется какойто «красоты».
вот так... через bash видел реализацию в 2010-м.
недавно видел реализацию через pbs,
где создавалась очередь pbs-файлов, но их там было поменьше...

p.s. это не задача как такова... а моя хотелка...

rgB
() автор топика

Читай книжку Using MPI. Там всё есть.

anonymous
()
Ответ на: комментарий от anonymous

Может ли const-метод вызывать не-const методы или функции?

не-const методы на this не может (ну с поправкой на const_cast), функции - смотря какие.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

А так хоть в интернет можно из константного метода за возвращаемым значением сгонять.

yoghurt ★★★★★
()
Ответ на: комментарий от anonymous

Звучит разумно, при условии, что const-метод всегда возвращает одинаковый результат.

Нет, возвращать он может что угодно. Дело в другом - const метод обязан не менять состояние объекта. Если в цикле не вызывается не-const методов объекта, то его состояние не меняется. Если состояние объекта не меняется, то его данные не нужно перезапрашивать каждую итерацию. Разумеется, это относится только к случаям когда известно что запрашиваются именно только данные объекта.

Я изначально не совсем точно выразился - дело не в том что size() const, а в том что в цикле не вызывается не-const методов, а size() инлайнится. Если бы в size() был rand, который не имеет отношения к данным объекта, оптимизации бы не было. Если бы тело size() не было известно на этапе компиляции (т.е. в нём мог бы оказаться тот же rand), то оптимизации бы не было.

На практике при read-only работе с контейнерами в циклах действительно не-const методов не вызывается, и size() инлайнится, поэтому ручное сохранение размера в const size_t - бесполезная ерунда усложнающая код из серии ++i вместо i++ для простых итераторов.

slovazap ★★★★★
()

хоть в интернет можно
Если бы в size() был rand, который не имеет отношения к данным объекта, оптимизации бы не было

Ну вот, теперь всё предельно ясно :)

yoghurt, slovazap, сердечно благодарю.

Пытался однажды выучиь C++, но дальше хэллоуворлда не ушёл. А сейчас мне там и вовсе не продраться, вон, там и лямбды и смарт-поинтеры, автовыведение типов с дженериками, а в следующей версии и вовсе конкуренцию всякую с корутинами обещают. Не... мне мой питончик милее :) Хоть в нём тоже почти всё это есть, но оно как-то более естественно, что ли, и легче усваивается.

anonymous
()
Ответ на: комментарий от anonymous

можно же не лезть выше c++11...
для каждой задачи свои методы...
вот я слез с матлабa на питон...

rgB
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.