Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Математическое моделирование в астрофизике. В 2 частях. Ч.2. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
873 Кб
Скачать

int source , MPI_Comm comm );

Значения count, datatype, source, comm должны быть одинаковы у всех процессов.

2. Cборка данных со всех процессов в буфере rbuf процесса dest (в порядке возрастания номера процесса):

MPI_Gather ( void * sbuf , int scount , MPI_Datatype stype , void * rbuf , int rcount , MPI_Datatype rtype , int

dest , MPI_Comm comm );

3. обратная операция (рассылка данных от процесса source всем остальным):

MPI_Scatter ( void * sbuf , int scount , MPI_Datatype stype , void * rbuf , int rcount , MPI_Datatype rtype , int source , MPI_Comm comm );

Все вышеперечисленные операции осуществляются с блокировкой. Пример применения операции MPI_Bcast приведен в листинге 4.13, а примеры применения операций MPI_Gather и MPI_Scatter – в листингах 4.14 и 4.15. В листинге 4.14 массивы sendarray посылаются процессу root всеми процессами, в том числе самим root, и объединяются в массиве rbuf. Память под массив rbuf выделяется только в принимающем процессе. В листинге 4.15 процесс root раздает всем процессам, включая себя, по сто элементов массива sendbuf.

Листинг 4.13. Программа с использованием процедуры MPI_Bcast

для рассылки текстового сообщения всем процессам от процесса

 

 

с номером 1

1

# include

" mpi .h"

2

# include

< stdio .h >

3

# include

< string .h >

4

 

 

5

int main ( int argc , char ** argv )

6

{

 

81

7

8

9

int numtasks , rank , root ;

char sbuf [20];

strcpy (sbuf , "I am not root \0");

10 root = 1;

11

12MPI_Init (& argc , & argv );

13MPI_Comm_size ( MPI_COMM_WORLD , & numtasks );

14MPI_Comm_rank ( MPI_COMM_WORLD , & rank );

15

16if ( rank == root )

17strcpy (sbuf , " Hello from root \0");

18MPI_Bcast (sbuf , 16, MPI_CHAR , root , MPI_COMM_WORLD );

19

20if ( rank == root )

21strcpy (sbuf , "I am root \0");

22printf ("I am %d. Message received : %s\n", rank , sbuf );

23

24

MPI_Finalize ();

25

return 0;

26

}

 

 

Листинг 4.14. Фрагмент программы с использованием

 

 

процедуры MPI_Gather

1

MPI_Comm comm ;

2

int

gsize , sendarray [100];

3

int

root ;

4

int * rbuf ;

5 ...

 

6

MPI_Comm_rank (comm , & myrank );

7

if

( myrank == root )

8

{

 

82

9MPI_Comm_size ( comm , & gsize );

10

rbuf = new int [ gsize * 100];

11

}

12

MPI_Gather ( sendarray , 100 , MPI_INT , rbuf , 100 ,

 

MPI_INT , root , comm );

Листинг 4.15. Фрагмент программа с использованием процедуры MPI_Scatter

1

MPI_Comm comm ;

2

int

gsize ;

3

int *

sendbuf ;

4

int

root , rbuf [100];

5...

6

MPI_Comm_rank ( comm , & myrank );

7

if ( myrank == root )

8

{

9MPI_Comm_size ( comm , & gsize );

10 sendbuf = new int [ gsize * 100];

11}

12...

13MPI_Scatter ( sendbuf , 100 , MPI_INT , rbuf , 100 , MPI_INT , root , comm );

Следует упомянуть о типах данных для пересылки. Существуют встроенные типы MPI_INT, MPI_FLOAT, MPI_DOUBLE, MPI_CHAR и т. д., однако на их основе разработчик может конструировать свои собственные. Производные типы данных являются структурами, состоящими из основных типов, например смежный тип:

MPI_Type_contiguous ( int count , MPI_Datatype oldtype , MPI_Datatype * newtype );

Эта процедура создает из типа oldtype тип newtype, объединяя count

элементов этого типа, что удобно при пересылке участков массивов, хранящихся в памяти последовательно.

83

Можно создать и более сложный тип данных – маску, позволяющую пересылать элементы массива не подряд, а с некоторым интервалом между ними (например, только нечетные):

int MPI_Type_vector ( int count , int blocklength , int stride , MPI_Datatype oldtype , MPI_Datatype * newtype );

Данная процедура создает тип для пересылки count участков массива, причем количество элементов каждого пересылаемого участка равно blocklength, а количество элементов между началами участков равно

stride.

Следующая процедура делает маску еще сложнее: теперь длина каждого участка и расстояние между ними задаются индивидуально при помощи массивов array_of_blocklengths и array_of_displacements:

int MPI_Type_indexed ( int count , int *

array_of_blocklengths , int * array_of_displacements , MPI_Datatype oldtype , MPI_Datatype * newtype );

Структурный тип объединяет для пересылки элементы различных ти-

пов:

int MPI_Type_struct ( int count , int * array_of_blocklengths , MPI_Aint *

array_of_displacements , MPI_Datatype * array_of_types , MPI_Datatype * newtype );

Пример создания и использования типа для пересылки частиц в кинетическом коде с использованием структурного типа приведен в листинге 4.16.

Листинг 4.16. Фрагмент программы с созданием типа данных

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

1 // special structure for sending particle data

2 typedef struct tagSENDPART

3 {

4 int sort ; // sort of particle

84

5

double

v [3];

// velocity

 

6

double

r [3];

// position

in space

7

} SENDPART ;

 

 

8

 

 

 

 

9

void Build_MPI_Type_SENDPART ( MPI_Datatype * newtype )

10{

11SENDPART * p = new SENDPART ;

12int count = 3;

13int blocklengths [3];

14MPI_Aint displacements [3];

15MPI_Aint addresses [4];

16MPI_Datatype types [3];

17

types [0]

=

MPI_INT ; // sort

18

types [1]

=

MPI_DOUBLE ;

// v

19

types [2]

=

MPI_DOUBLE ;

// r

20

blocklengths [0]

=

1;

 

21

blocklengths [1]

=

3;

 

22

blocklengths [2]

=

3;

 

23

MPI_Address (p , & addresses [0]) ;

 

 

24

MPI_Address (&(p -> sort ) , & addresses [1]) ;

25

MPI_Address (&(p ->v) ,

& addresses [2]) ;

26

MPI_Address (&(p ->r) ,

& addresses [3]) ;

27

displacements [0]

=

addresses [1]

-

addresses [0];

28

displacements [1]

=

addresses [2]

-

addresses [0];

29

displacements [2]

=

addresses [3]

-

addresses [0];

30MPI_Type_struct ( count , blocklengths , displacements , types , newtype );

31MPI_Type_commit ( newtype );

32delete p;

33}

34...

35 int main ( int argc , char ** argv )

36 {

85

37...

38MPI_Datatype MPI_SENDPART ;

39Build_MPI_Type_SENDPART (& MPI_SENDPART );

40...

41 MPIsend ( parts , 4, MPI_SENDPART , dest , tag ,

MPI_COMM_WORLD );

42...

43MPI_Type_free ( MPI_SENDPART );

44...

45}

Вкодах с пространственной дискретизацией при распараллеливании часто создаются дополнительные ячейки для пересылок (ghost cells) (рис. 4.5). При вычислении градиентов эти ячейки соседствуют с граничными ячейками. В дополнительные ячейки периодически присылается актуальная информация о полях, плотностях и токах в граничных ячейках соседнего процесса.

Во многих случаях технологии векторизации, OpenMP и MPI можно совмещать, т. е. векторизовать операции над массивами, распараллеливать с помощью OpenMP внешние циклы и осуществлять коммуникацию между узлами при помощи технологии MPI. Число ядер в современных вычислительных комплексах может достигать сотен тысяч. Благодаря этому техно-

Рис. 4.5. Дополнительные ячейки для пересылок в кодах с пространственной дискретизацией

86

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

Упражнения

1. Оптимизировать программу, считающую ускорение в магнитном поле, энергию и импульс массива частиц. С помощью библиотеки time.h определить время работы программы до и после оптимизации, усредненное по нескольким запускам.

Текст программы:

1

# include

 

" time .h"

 

 

 

2

# include

 

" math .h"

 

 

 

3

# include

 

" stdio .h"

 

 

 

4

 

 

 

 

 

 

 

 

5

int NP

=

 

1000;

 

 

 

6

double

c

 

=

3 e10 ;

 

 

 

7

double mp = 1.67 e -24;

 

 

8

double me = 9.1e -28;

 

 

 

9

double q = 4.8e -10;

 

 

 

10

double *

 

v

=

new double [ NP ];

// velocity

11

double *

 

a

=

new double [ NP ];

// acceleration

12

double *

 

E

=

new double [ NP ];

// energy

13

double *

 

p

=

new double [ NP ];

// momentum

14

double

Vmax

= 1 e8 ;

// max

velocity

15

 

 

 

 

 

 

 

 

16

double GetRand ( double min ,

 

double max )

17

{

 

 

 

 

 

 

 

18

return

 

( min + ( max

- min )

* rand () / ( RAND_MAX +

 

1.0) );

 

 

 

 

19

}

 

 

 

 

 

 

 

20

 

 

 

 

 

 

 

 

21

int main ( int

argc , char **

argv )

22

{

 

 

 

 

 

 

 

87

a[i] = -q * v[i] * me / c;
E[i] = me * pow (v[i], 2) / 2; p[i] = sqrt (2 * me * E[i ]) ;

23double worktime ;

24// initialization

25

for

( int

i

=

0;

i

<

NP ; i ++)

26

{

 

 

 

 

 

 

 

 

27

v[i] =

GetRand (0 ,

Vmax );

28

}

 

 

 

 

 

 

 

 

29

// optimize

only

this

 

cycle !

 

30

for

( int

i

=

0;

i

<

NP ; i ++)

31

{

 

 

 

 

 

 

 

 

32

if

(i %

2

==

0)

 

 

 

33

{

 

 

 

 

 

 

 

 

34

 

a[i] = q * v[i] * mp / c;

35

 

E[i]

=

mp * pow (v[i],

2) / 2;

36

 

p[i]

=

sqrt (2

*

mp *

E[i ]) ;

37}

38else

39{

40

41

42

43}

44}

45printf (" total work time = %f seconds \n", worktime );

46}

2.Откомпилировать программу с флагом векторизации. Выяснить, векторизуется ли цикл. Если нет – исправить ситуацию (возможно, с применением директив OpenMP). С помощью библиотеки time.h определить время работы программы с векторизацией и без.

 

Текст программы:

1

# include " time .h"

2

# include

" math .h"

3

# include

" stdio .h"

88

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

int N

= 100000;

 

 

 

 

 

 

double

func ( double

x ,

double

y , double

z)

{

 

 

 

 

 

 

 

 

 

if (z > 0)

 

 

 

 

 

 

 

return x * x + sqrt (y);

 

 

else

 

 

 

 

 

 

 

 

 

return sin (x)

+ sqrt (y);

 

 

}

 

 

 

 

 

 

 

 

 

int main ( int argc ,

char ** argv )

 

{

 

 

 

 

 

 

 

 

 

double worktime ;

 

 

 

 

 

double * A = new double [N ];

 

 

double * B = new double [N ];

 

 

for

( int

i

=

0;

i <

N;

i ++)

 

 

A[i] = double (i);

 

 

 

 

for

( int

i

=

0;

i <

N;

i ++)

 

 

{

 

 

 

 

 

 

 

 

 

if

(( i

!=

N

-

1)

&&

(i != 0) )

 

 

B[i]

=

func (A[i],

A[i +

1] , A[i

- 1]) ;

26}

27printf (" total work time = %f seconds \n", worktime );

28}

3.Распараллелить методом OpenMP программу подсчета суммарной энергии массива частиц. С помощью библиотеки time.h определить время счета до и после распараллеливания.

Текст программы:

#include " time .h"

#include " math .h"

#include " stdio .h"

89

int NP = 100000;

 

 

 

double GetRand ( double min ,

double max )

{

 

 

 

 

return ( min

+ ( max

-

min ) * rand () / ( RAND_MAX +

1.0) );

 

 

 

 

}

 

 

 

 

int main ( int

argc , char **

argv )

{

 

 

 

 

double worktime ;

 

 

 

double m2 = 0.5;

 

 

 

double v = 0.0;

 

 

 

double E = 0.0;

 

 

 

double Vmax = 1.0;

 

 

 

for ( int i

= 0; i

<

NP ;

i ++)

{

 

 

 

 

v = GetRand ( Vmax );

 

 

E += m2 *

v * v;

 

 

 

}

printf ("E = %f work time = %f seconds \n", E, worktime );

}

4. Распараллелить методом OpenMP и векторизовать программу суммирования элементов массива. С помощью библиотеки time.h определить время счета до и после распараллеливания.

Текст программы:

#include " time .h"

#include " math .h"

#include " stdio .h"

90

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]