Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
MPI
Лекция 2
Отложенная инициализация обмена
• MPI_Send_init(buf,count,datatype,dest,comm,request) – информирование операционной системы о производящихся обменах.
• MPI_Start(request)/MPI_Start_all(cont,requests) –
Непосредственный запуск обмена. По выходу из функции память можно изменить и вызвать MPI_Start повтороно.
• MPI_Request_free(request) – Освобождает зарезервированные в операционной системе буфер для обмена
Упаковка и распаковка данных
MPI
MPI_Pack_size
Запрос размера буфера для
упаковываемых переменных
Память
Резервирование памяти
MPI_Pack/MPI_Unpack
Упаковка и распаковка данных• MPI_Pack_size(incount,datatype,comm,&size) –
добавляет к size размер буфера необходимый для упаковки данных.
• MPI_Pack(inbuf,incount,datatype,outbuf,
outcount,position,comm) – Упаковывает данные в outbuf и возвращает позицию в буфере.
• MPI_Unpack – распаковывает данные из буфера, причём в том порядке в каком они были упакованны.
Пример с упаковкой/распаковкой.#include <mpi.h>#include <stdio.h>#include <stdlib.h>
int main(int argc,char **argv){ int rank,size,buf_size=0,pozition=0;
MPI_Status status;
void *buf=NULL; Int a=1; char *b=“word”;
MPI_Init(&argc,&argv);MPI_Comm_size(MPI_COMM_WORLD,&size);MPI_Comm_rank(MPI_COMM_WORLD,&rank);
Пример c упаковкой/распаковкойIf(rank==0) { MPI_Pack_size(1,MPI_INT,MPI_COMM_WORLD,&buf_size); MPI_Pack_size(strlen(b)+1,MPI_CHAR,MPI_COMM_WORLD,&buf_size); buf=malloc(size); MPI_Pack(a,1,MPI_INT,buf,buf_size,&pozition,MPI_COMM_WORLD); MPI_Pack(b,strlen(b)+1,MPI_CHAR,buf,buf_size,&pozition,MPI_COMM_WORLD); MPI_Send(buf,buf_size,MPI_BYTE,1,0,MPI_COMM_WORLD); }
If(rank==1) { MPI_Pack_size(1,MPI_INT,MPI_COMM_WORLD,&buf_size); MPI_Pack_size(strlen(b)+1,MPI_CHAR,MPI_COMM_WORLD,&buf_size); buf=malloc(size); MPI_Recv(buf,buf_size,MPI_BYTE,1,0,MPI_COMM_WORLD,&status) MPI_Unpack(a,1,MPI_INT,&pozition,buf,buf_size,MPI_COMM_WORLD); MPI_Unpack(b,strlen(b)+1,MPI_CHAR,&pozition,buf,buf_size,pozition,MPI_COMM_WORLD); printf(“%d ‘%s’\n”,a,b); } MPI_Finalize(); return 0;}
Упаковка во внешний формат(MPI-2)
• Необходима для передачи данных для разных реализаций MPI. Данные сохраняются в формате «extern32».
• Задаётся суффиксом _extern для вызовов функций упаковки и распаковки– MPI_Pack_extern– MPI_Pack_size_extern
Собственные типы данных
• MPI_Type_contigious(count,oldtype,newtype) задает тип – непрерывную конкатенацию значений типа oldtype (массив), newtype и oldtype – переменные типа MPI_Datatype
• MPI_Type_vector(count,blocklength,stride,oldtype,newtype) – задаёт новый тип данных с count блоками старого типа по blocklength элементов, где расстояния между началами блоков stride. Таким образом возможны наложения.
Собственные типы данных
• MPI_Type_struct(count,blocklens[],offsets[],old_types,*newtype) – отличается от вектора тем, что старых типов данных может быть несколько.
• MPI_Type_extent(type) – размер нового типа в байтах.
• MPI_Type_commit(type) – регистрирует свежесозданный тип для данного процесса MPI программы.
• MPI_Type_free(type) – освобождает занятый тип данных, после этого можно создавать тип поновый.
Пример создания типа#include <mpi.h>#include <stdio.h>#define SIZE 4int main(argc,argv) int argc; char *argv[]; { int numtasks, rank, source=0, dest, tag=1, i; float a[SIZE][SIZE] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0};float b[SIZE];MPI_Status stat;MPI_Datatype rowtype;
MPI_Init(&argc,&argv);MPI_Comm_rank(MPI_COMM_WORLD, &rank);MPI_Comm_size(MPI_COMM_WORLD, &numtasks);
MPI_Type_contiguous(SIZE, MPI_FLOAT, &rowtype);MPI_Type_commit(&rowtype);
if (numtasks == SIZE) { if (rank == 0) { for (i=0; i<numtasks; i++) MPI_Send(&a[i][0], 1, rowtype, i, tag, MPI_COMM_WORLD); } MPI_Recv(b, SIZE, MPI_FLOAT, source, tag, MPI_COMM_WORLD, &stat); printf("rank= %d b= %3.1f %3.1f %3.1f %3.1f\n", rank,b[0],b[1],b[2],b[3]); }else printf("Must specify %d processors. Terminating.\n",SIZE);
MPI_Finalize();}
Группы и области связи
• Группа – упорядоченное множество процессов. Каждый процесс группы имеет номер (rank). Внутри группы возможны групповые операции.
• Область связи (communication domain) – множество связей между процессами.
Коммуникаторы
Коммуникатор – локальное представление области связи (массив ссылок на все процессы о.с.)
Область связи задается множеством коммуникаторов (по одному на каждом процессоре)
Консистентность коммуникаторов
Коммуникаторы• Внутренний (intracommunicator) – обмены внутри
своей группы. Атрибуты:
1.группа процессов2.виртуальная топология
• Межгрупповой (intercommunicator) – обмены между процессами разных групп.
int MPI_Intercomm_create(
MPI_Comm local_comm,
int local_leader,
MPI_Comm peer_comm,
int remote_leader, int tag,
MPI_Comm *comm_out)
Группы и коммуникаторы
Группы• MPI_Comm_group (comm,group ) –
возвращается группа заданного коммуникатора.
• MPI_Group_rank(group,rank) - возвращает порядковый номер в группе, или MPI_UNDEFINED если процесс не является членом группы.
• MPI_Group_size(group,size) – возвращает размер группы.
Создание группы.• MPI_Group_incl(group,n,ranks,newgroup) – Создаёт
новую группу с n членами и номерами ranks[0] ... ranks[n-1]
• MPI_Group_excl – тоже самое, но добавляет в группу не указанные в ranks процессы.
• MPI_Group_intersection(group1,group2,newgroup) – пересечение 2-х групп.
• MPI_Group_union – объединение 2-х групп.
• MPI_Group_diference – вычитание 2-х групп.
Сравнение и уничтожение группы.
• MPI_Group_compare(group1,group2,result) – говорит совпадают ли группы или нет.
• MPI_Group_free(group) – уничтожает группу.
Создание коммуникаторов.
• MPI_Comm_create(comm,group,newcomm) – создаёт новый коммуникатор для указанной группы. group – подмножество группы коммуникатора comm. Возвращает MPI_COMM_NULL на процессах, не входящих в group. Ошибка, если разные group.
• MPI_Comm_dup(comm,newcomm) – дублирует коммуникатор со всеми группами и топологией.
• MPI_Comm_split(comm,color,key,newcomm) – разделяет процессы по цветам, соединяет их в одноцветные группы и упорядочивает внутри групп по возрастанию ключа. Можно передать цвет MPI_UNDEFINED.
Освобождение коммуникатора
• MPI_Comm_free(comm) – освобождает коммуникатор.
• Создание группы – локальная операция (не требует обменов)
• Создание коммуникатора – групповая операция (требуются обмены для проверки консистентности задания области связи)
Пример с коммуникаторами.#include <mpi.h>#include <stdio.h>#define NPROCS 8int main(argc,argv) int argc; char *argv[];{ int rank,new_rank,sendbuf,recvbuf,numtasks; int ranks1[4]={0,1,2,3},ranks2[4]={4,5,6,7}; MPI_Group orig_group, new_group; MPI_Comm new_comm;
MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); if (numtasks != NPROCS) { printf("Must specify MP_PROCS= %d. Terminating.\n",NPROCS); MPI_Finalize(); exit(0); } sendbuf = rank; /* Extract the original group handle */ /* Divide tasks into two distinct groups based upon rank */ MPI_Comm_group(MPI_COMM_WORLD, &orig_group);
Пример с коммуникаторами. if (rank < NPROCS/2) MPI_Group_incl(orig_group, NPROCS/2, ranks1, &new_group); else MPI_Group_incl(orig_group, NPROCS/2, ranks2, &new_group); /* Create new new communicator and then perform collective communications */ MPI_Comm_create(MPI_COMM_WORLD, new_group, &new_comm);
MPI_Allreduce(&sendbuf, &recvbuf, 1, MPI_INT, MPI_SUM,new_comm); MPI_Group_rank (new_group, &new_rank); printf("rank= %d newrank= %d recvbuf=%d\n", rank,new_rank,recvbuf); MPI_Finalize();}
Топология• Cart – топология n-мерной решётки.
• Graph – задание произвольного графа.
Топология решётки• MPI_Cart_create(comm_old,ndims,dims,periods,reord
er,comm_cart) – Возвращает новый коммуникатор, где процессы объеденины в n-мерную решётку.
ndims – размерность решётки (2 мерная к примеру)
dims – число процессов по каждому измерению. periods – массив true/false говорит, что решётка
замкнута по данному измерению. (В результате получаем кольца торы и.т.п)
reorder – разрешает или нет переупорядочивать порядковые номера процессов.
Топология решётки
Топология решётки
• MPI_Cart_get(comm,ndims,dims,periods,coords) – по коммуниратору и заданной размерности решётки выдаёт в dims размерность по каждому измерению, зациклено ли по измерению, и координаты данного процесса.
• MPI_Cart_map(comm, ndims,dims,periods,newrank) - выдаёт рекомендованный MPI порядковй номер процесса для решётки.
Топология решётки
• MPI_Cart_shift(comm,direction,disp,rank_source,rank_dest) – выдаёт порядковые номера процессов в решётке, для заданного разрешения и заданного смещения disp.
Топология решётки.
• MPI_Cart_coords(MPI_Comm comm, int rank, int maxdims, int *coords) – выдаёт координаты в решётке по порядковому номеру.
• MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank) – выдаёт порядковый номер по координатам.
• MPI_Cartdim_get(comm,ndims) – возвращает в ndims размерность сетки.
Пример с топологией решёткой#include<mpi.h>void main(int argc, char *argv[]) { int rank; MPI_Comm vu; int dim[2]={4,3}; int period[2]={TRUE,FALSE}; int reorder=TRUE; int coord[2]={3,1}; int id; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Cart_create(MPI_COMM_WORLD,2,dim,period,reorder,&vu); if(rank==5){ MPI_Cart_coords(vu,rank,2,coord); printf("P:%d My coordinates are %d %d\n",rank,coord[0],coord[1]); } if(rank==0) { MPI_Cart_rank(vu,coord,&id); printf("The processor at position (%d, %d) has rank %d\n",coord[0],coord[1],id); } MPI_Finalize();}
Пример с топологией решёткой#include <stdio.h>#include "mpi.h"
int main( argc, argv )int argc;char **argv;{ int rank, value, size, false=0; int right_nbr, left_nbr; MPI_Comm ring_comm; MPI_Status status;
MPI_Init( &argc, &argv );
MPI_Comm_size( MPI_COMM_WORLD, &size ); MPI_Cart_create( MPI_COMM_WORLD, 1, &size, &false, 1, &ring_comm ); MPI_Cart_shift( ring_comm, 0, 1, &left_nbr, &right_nbr ); MPI_Comm_rank( ring_comm, &rank ); MPI_Comm_size( ring_comm, &size ); do {
if (rank == 0) { scanf( "%d", &value ); MPI_Send( &value, 1, MPI_INT, right_nbr, 0, ring_comm );}else { MPI_Recv( &value, 1, MPI_INT, left_nbr, 0, ring_comm,
&status ); MPI_Send( &value, 1, MPI_INT, right_nbr, 0, ring_comm );}printf( "Process %d got %d\n", rank, value );
} while (value >= 0);
MPI_Finalize( ); return 0;}
Топология Граф• MPI_Graph_create(MPI_Comm comm_old, int
nnodes, int *index, int *edges, int reorder, MPI_Comm *comm_graph) – Создаёт граф с указанным числом узлов, заданными вершинами и рёбрами.
edges 2 1
0
3
1 2 4 5
1 2 3 1 3 0
index
Топология граф.
• MPI_Graph_neighbors_count(MPI_Comm comm, int rank, int *nneighbors) – подсчитывает число соседей.
• MPI_Graphdims_get(MPI_Comm comm, int *nnodes, int *nedges) – возвращает число вершин и число рёбер в графе.
Топология граф
• MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges, int *index, int *edges) – сбор информации о графе.
• MPI_Graph_neighbors(comm,rank,maxneighbors,*neighbors[]) – соседи для процесса.
Топология
• MPI_Topo_test (comm,top_type) – возвращает тип топологии для коммуникатора.
Фиксация событий в (mpich)Фиксация событий во время выполнения
программы производится при помощи библиотеки MPE. Необходимо откомпилировать программу с одним из ключей:
-mpilog – замеряет время всех MPI вызовов и кладёт всё это в файл.
-mpitrace – также замеряет время и выводит всё это в stdout
-mpianim – показ работы программы в данный момент времени (в реальном времени.)
Log файлы в mpich
Имеется 3 формата log файлов:alog – формат формат, который можно
подать на вход upshot.сlog – бинарный формат.slog – формат, который можно подать на
вход jumpshot.
Показовалки log файлов в MPICH
• upshot – приложение показывает трассу.
• jumpshot-4 – Java приложение умеет показывать трассу и статистику по вызовам.
MPI-2
Возможности MPI-2
• Все возможности MPI-1• Динамическое порождение процессов• Коммуникации в стиле TCP• Односторонние коммуникации по типу
Remote Memory Access• Параллельная работа с файлами
Запуск MPI-2 программы
– mpiexec -n 16 prog args• Запуск 16 процессов на текущей машине или на
машине по умолчанию
– mpiexec -n 5 -arch sun -host scluster ocean : -n 10 -arch rs6000 -host ibmcluster atmos• Запускает разные бинарные файлы на нескольких
машинах
– mpiexec -configfile config_file• читает параметры из конфиг файла: двоеточия
заменяются на перевод строки.
Процессы в MPI-2
• Модель процессов в MPI-2 позволяет запускать и завершать процессы после запуска MPI приложения.
• Предоставляет механизм установки взаимодействий между старыми процессами и вновь созданными.
Динамическое порождение процессов
• MPI_Comm_spawn(command, argv,maxprocs,MPI_Info info,root,comm, MPI_Comm *intercomm, array_of_errcodes)
command - имя запускаемой программыargv – аргументы командной строкиmaxprocs – максимальное число порождаемых процессовinfo – множество пар (host, number) процессор и порядковый
номер на нём.root – где параметр info будет читаться.comm – внутренний коммуникатор (например MPI_COMM_WORLD
который актуален только для порождённых процессов)intercomm – коммуникатор для общения между родителем и
порождёнными процессами.
Динамические процессы.
• int MPI_Comm_get_parent(MPI_Comm *parent) – возвращает родительский коммуникатор.
• int MPI_Comm_spawn_multiple(count, array_of_commands, аrray_of_argv,array_of_maxprocs, array_of_info,root,intercomm,array_of_errcodes)– прождает процессы из разных бинарников.
Пример с динамическим порождением процессов
• /* manager */ • #include "mpi.h“• int main(int argc, char *argv[])• { • int world_size, universe_size, *universe_sizep, flag;• MPI_Comm everyone; /* intercommunicator */• char worker_program[100];• MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &world_size); • if (world_size != 1) error("Top heavy with management"); • MPI_Attr_get(MPI_COMM_WORLD, MPI_UNIVERSE_SIZE, &universe_sizep,
&flag);• if (!flag) • {• printf("This MPI does not support UNIVERSE_SIZE. How many\n\ processes
total?");• scanf("%d", &universe_size);• }• else universe_size = *universe_sizep;
Пример с динамическим порождением процессов
• if (universe_size == 1) error("No room to start workers"); • /* *
Now spawn the workers. Note that there is a run-time determination of what type of worker to spawn, and presumably this calculation must be done at run time and cannot be calculated before starting the program. If everything is known when the application is first started, it is generally better to start them all at once in a single MPI_COMM_WORLD.
*/
• choose_worker_program(worker_program); MPI_Comm_spawn(worker_program, MPI_ARGV_NULL, universe_size-1, MPI_INFO_NULL, 0, MPI_COMM_SELF, &everyone, MPI_ERRCODES_IGNORE);
• /* * Parallel code here. The communicator "everyone" can be used to communicate with the
spawned processes, which have ranks 0,.. MPI_UNIVERSE_SIZE-1 in the remote group of the intercommunicator "everyone".
*/ • MPI_Finalize(); return 0; }
Пример с динамическим порождением процессов
• /* worker */ • #include “mpi.h”• int main(int argc, char *argv[])• {
int size; MPI_Comm parent; MPI_Init(&argc, &argv);MPI_Comm_get_parent(&parent);
if (parent == MPI_COMM_NULL) error("No parent!");MPI_Comm_remote_size(parent, &size);if (size != 1) error("Something's wrong with the parent");
/* * Parallel code here. The manager is represented as the process with rank 0 in (the remote group of)
MPI_COMM_PARENT. If the workers need to communicate among themselves, they can use MPI_COMM_WORLD.
*/ MPI_Finalize(); return 0;
• }
Работа с файлами
• MPI_File_open(MPI_Comm comm, char *filename, int amode, MPI_Info info, MPI_File *fh)
• MPI_File_close(MPI_File *fh)• int MPI_File_preallocate(MPI_File fh, MPI_Offset size)• MPI_File_get_size(MPI_File fh, MPI_Offset *size) • MPI_File_set_view(MPI_File fh, MPI_Offset disp, MPI_Datatype
etype, MPI_Datatype filetype, char *datarep, MPI_Info info)• MPI_File_read_at(MPI_File fh, MPI_Offset offset, void *buf, int
count, MPI_Datatype datatype, MPI_Status *status) • MPI_File_write_at(MPI_File fh, MPI_Offset offset, void *buf, int
count, MPI_Datatype datatype, MPI_Status *status)