Программирование для многопроцессорных систем в стандарте MPI - Шпаковский Г.И., Серикова Н.В
..pdf8.Добавить для всех конфигураций библиотеку ws2_32.lib (Это библиотека Microsoft Winsock2 library. Она по умолчанию находится в вашем пути к биб-
лиотекам). Добавить mpich.lib в режим release и mpichd.lib в режим debug.
281
9.Закрыть диалоговое окно установки проекта.Добавить к проекту исходные файлы.
10. Build.
Запуск приложения обычно производится из командной строки с помощью оператора mpirun, например:
mpi run -np 4 cpi
В этой команде могут быть также указаны различные опции, связанные с отладкой приложений, спецификой выполнения для различных устройств и платформ. Более подробно запуск приложений рассмотрен в главе 2.
Приложение 4. ХАРАКТЕРИСТИКИ КОММУНИКАЦИОННЫХ СЕТЕЙ ДЛЯ КЛАСТЕРОВ
Ниже приведены сравнительные характеристики некоторых сетей.
|
|
|
|
|
Таблица 1 |
Характеристики некоторых коммуникационных технологий |
|||||
|
|
|
|
|
|
|
SCI |
Myrinet |
cLAN |
ServerNet |
Fast |
|
|
|
|
|
Ethernet |
Латентность |
5,6 мкс |
17 мкс |
30 мкс |
13 мкс |
170 мкс |
Пропускная спо- |
80 |
40 |
100 |
180 |
10 |
собность ( MPI ) |
Мбайт/c |
Мбайт/c |
Мбайт/c |
Мбайт/c |
Мбайт/c |
Пропускная спо- |
400 |
160 |
150 |
н/д |
12,5 |
собность (аппа- |
Мбайт/c |
Мбайт/c |
Мбайт/c |
|
Мбайт/c |
ратная) |
|
|
|
|
|
Реализация MPI |
ScaMPI |
HPVMи др. |
MPI /Pro |
MVICH |
MPICH |
282
Коммуникационные сети. Производительность коммуникационных сетей в кластерных системах определяют две основные характеристики: латентность – время начальной задержки при посылке сообщений и пропускная способность сети, определяющая скорость передачи информации по каналам связи. При этом важны не столько пиковые характеристики, заявляемые производителем, сколько реальные, достигаемые на уровне пользовательских приложений, например, на уровне MPI–приложений. В частности, после вызова пользователем функции посылки сообщения Send() сообщение последовательно пройдет через целый набор слоев, определяемых особенностями организации программного обеспечения и аппаратуры, прежде, чем покинуть процессор - отсюда и вариации на тему латентности. Кстати, наличие латентности определяет и тот факт, что максимальная скорость передачи по сети не может быть достигнута на сообщениях с небольшой длиной.
Коммуникационная технология Fast Ethernet. Сети этого типа использу-
ются чаще всего благодаря низкой стоимости оборудования. Однако большие накладные расходы на передачу сообщений в рамках Fast Ethernet приводят к серьезным ограничениям на спектр задач, которые можно эффективно решать на таком кластере. Если от кластера требуется большая универсальность, то нужно переходить на другие, более производительные коммуникационные технологии.
Коммуникационная технология SCI. Основа технологии SCI – это кольца,
состоящие из быстрых однонаправленных линков c пиковой пропускной способностью на аппаратном уровне 400 Мбайт/c. Реальная пропускная способность на уровне MPI–приложений с использованием 32-разрядной шины PCI с частотой 33 МГц достигает 80 Мбайт/c, латентность – порядка 5,6 мкс.
Основной поставщик промышленных SCI–компонентов на современном рынке – норвежская компания Dolphin Interconnect Solutions. Вместе с компанией Scali Computer она предлагает интегрированное кластерное решение Wulfkit, в состав которого входят “основная” и “дочерняя” сетевые платы, два специальных кабеля и соответствующее программное обеспечение. Программный пакет Scali Software Platform включает средства конфигурирования и администрирования кластеров, и, что немаловажно, ScaMPI – оптимизированную под SCI реализацию интерфейса
MPI (Message Passing Interface). Поддерживаются операционные системы Linux, Solaris и NT
Существующие кластеры, построенные на основе технологии SCI, содержат до 100 узлов, в качестве которых используются одно-, двух- и четырехпроцессорные компьютеры на базе Intel или UltraSPARC. Все узлы объединяются в топологию “двухмерный тор”, образуемую двумя SCI-кольцами с использованием двух сетевых адаптеров на каждом узле. Одним из преимуществ подобного решения является отказ от дорогостоящих многопортовых коммутаторов. Самый большой кластер на базе SCI установлен в университете города Падеборн (Германия) – 96 двухпроцессорных узлов на базе Pentium II.
Коммуникационная технология Myrinet. Сетевую технологию Myrinet
представляет компания Myricom, которая впервые предложила свою коммуникационную технологию в 1994 году, а на сегодняшний день имеет уже более 1000 инсталляций по всему миру.
283
Узлы в Myrinet соединяются друг с другом через коммутатор (до 16 портов). Максимальная длина линий связи варьируется в зависимости от конкретной реализации. На данный момент наиболее распространены реализации сетей LAN и SAN. В последнем случае, при использовании в рамках вычислительной системы, длина кабеля не может превышать 3-х метров, а в LAN - 10,7 метра.
Приложение 5. ВАРИАНТЫ РЕШЕНИЯ ЗАДАНИЙ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ
Задание 3.1.
#include <stdio.h> #include "mpi.h"
int main( argc, argv ) int argc;
char **argv;
{int rank, size;
MPI_Init( &argc, &argv );
MPI_Comm_size( MPI_COMM_WORLD, &size ); MPI_Comm_rank( MPI_COMM_WORLD, &rank ); printf( "Hello world from process %d of %d\n", rank, size );
MPI_Finalize(); return 0;
}
Задание 3.3.
#include <stdio.h> #include "mpi.h"
int main( argc, argv ) int argc;
char **argv;
{int rank, value, size; MPI_Status status; MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size ); do
{if (rank == 0)
{scanf( "%d", &value );
MPI_Send( &value, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD );
}
else
{MPI_Recv( &value, 1, MPI_INT, rank - 1, 0, MPI_COMM_WORLD, &status ); if (rank < size - 1)
MPI_Send( &value, 1, MPI_INT, rank + 1, 0, MPI_COMM_WORLD );
}
284
printf( "Process %d got %d\n", rank, value ); } while (value >= 0);
MPI_Finalize( ); return 0; }
Задание 3.6.
#include "mpi.h" #include <stdio.h> int main(argc, argv) int argc;
char **argv;
{int rank, size, i, buf[1]; MPI_Status status; MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank ); MPI_Comm_size( MPI_COMM_WORLD, &size ); if (rank == 0)
{for (i=0; i<100*(size-1); i++)
{MPI_Recv( buf,1,MPI_INT,MPI_ANY_SOURSE, MPI_ANY_TAG,
MPI_COMM_WORLD, &status);
printf( "Msg from %d with tag %d\n", status.MPI_SOURCE, status.MPI_TAG );
}
}
else
{ for (i=0; i<100; i++)
MPI_Send( buf, 1, MPI_INT, 0, i, MPI_COMM_WORLD );
}
MPI_Finalize(); return 0;
}
Задание 3.13.
/* пересылка вектора из 1000 элементов типа MPI_DOUBLE
со страйдом 24 между элементами. Используем MPI_Type_vector */ #include <stdio.h>
#include <stdlib.h> #include "mpi.h"
#define NUMBER_OF_TESTS 10 int main( argc, argv )
int argc; char **argv;
{MPI_Datatype vec1, vec_n, old_types[2]; MPI_Aint indices[2];
double *buf, *lbuf, t1, t2, tmin; register double *in_p, *out_p;
int i, j, k, nloop, rank, n, stride, blocklens[2]; 285
MPI_Status status; MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank ); n = 1000; stride = 24; nloop = 100000/n;
buf = (double *) malloc( n * stride * sizeof(double) ); if (!buf)
{fprintf( stderr, "Could not allocate send/recv buffer of size %d\n",n * stride ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
lbuf = (double *) malloc( n * sizeof(double) ); if (!lbuf)
{fprintf( stderr, "Could not allocated send/recv lbuffer of size %d\n", n ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
if (rank == 0) printf( "Kind\tn\tstride\ttime (sec)\tRate (MB/sec)\n" );
/* создаем новый векторный тип с заданным страйдом */ MPI_Type_vector( n, 1, stride, MPI_DOUBLE, &vec1 );
MPI_Type_commit( &vec1 ); tmin = 1000;
for (k=0; k<NUMBER_OF_TESTS; k++)
{if (rank == 0)
{/* убедимся, что оба процесса готовы к приему/передаче */ MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 1, 14,
MPI_BOTTOM, 0, MPI_INT, 1, 14, MPI_COMM_WORLD, &status ); t1 = MPI_Wtime();
for (j=0; j<nloop; j++)
{
MPI_Send( buf, 1, vec1, 1, k, MPI_COMM_WORLD );
MPI_Recv( buf, 1, vec1, 1, k, MPI_COMM_WORLD, &status );
}
t2 = (MPI_Wtime() - t1) / nloop; ; /* время для пересылок*/ if (t2 < tmin) tmin = t2;
/* убедимся, что оба процесса готовы к приему/передаче */ MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 0, 14,
MPI_BOTTOM, 0, MPI_INT, 0, 14, MPI_COMM_WORLD, &status ); for (j=0; j<nloop; j++)
{MPI_Recv( buf, 1, vec1, 0, k, MPI_COMM_WORLD, &status ); MPI_Send( buf, 1, vec1, 0, k, MPI_COMM_WORLD );
}
}
}
tmin = tmin / 2.0; if (rank == 0)
printf( "Vector\t%d\t%d\t%f\t%f\n", n, stride, tmin, n*sizeof(double)*1.0e-6 / tmin );
286
MPI_Type_free( &vec1 ); MPI_Finalize( );
return 0;
}
Задание 3.14.
/* пересылка вектора из 1000 элементов типа MPI_DOUBLE
со страйдом 24 между элементами. Используем MPI_Type_struct */ #include <stdio.h>
#include <stdlib.h> #include "mpi.h"
#define NUMBER_OF_TESTS 10 int main( argc, argv )
int argc; char **argv;
{ MPI_Datatype vec1, vec_n, old_types[2];
MPI_Aint |
indices[2]; |
|
double |
*buf, *lbuf, t1, t2, tmin; |
|
register double *in_p, *out_p; |
||
int |
i, j, k, nloop, rank, n, stride, blocklens[2]; |
|
MPI_Status |
status; |
|
MPI_Init( &argc, &argv ); |
MPI_Comm_rank( MPI_COMM_WORLD, &rank ); n = 1000;
stride = 24;
nloop = 100000/n;
buf = (double *) malloc( n * stride * sizeof(double) ); if (!buf)
{ fprintf( stderr, "Could not allocate send/recv buffer of size %d\n",n * stride ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
lbuf = (double *) malloc( n * sizeof(double) ); if (!lbuf)
{ fprintf( stderr, "Could not allocated send/recv lbuffer of size %d\n", n ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
if (rank == 0) printf( "Kind\tn\tstride\ttime (sec)\tRate (MB/sec)\n" ); /* создаем новый тип */
blocklens[0] = 1; |
blocklens[1] = 1; |
indices[0] = 0; |
indices[1] = stride * sizeof(double); |
old_types[0] = MPI_DOUBLE; old_types[1] = MPI_UB; MPI_Type_struct( 2, blocklens, indices, old_types, &vec_n ); MPI_Type_commit( &vec_n );
tmin = 1000;
for (k=0; k<NUMBER_OF_TESTS; k++)
{ if (rank == 0) { /* убедимся, что оба процесса готовы к приему/передаче */ 287
MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 1, 14,
MPI_BOTTOM, 0, MPI_INT, 1, 14, MPI_COMM_WORLD, &status ); t1 = MPI_Wtime();
for (j=0; j<nloop; j++)
{MPI_Send( buf, n, vec_n, 1, k, MPI_COMM_WORLD ); MPI_Recv( buf, n, vec_n, 1, k, MPI_COMM_WORLD, &status );
}
t2 = (MPI_Wtime() - t1) / nloop; ; /* время для пересылок*/ if (t2 < tmin) tmin = t2;
}
else
if (rank == 1)
{ /* убедимся, что оба процесса готовы к приему/передаче */ MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 0, 14,
MPI_BOTTOM, 0, MPI_INT, 0, 14, MPI_COMM_WORLD, &status ); for (j=0; j<nloop; j++)
{MPI_Recv( buf, n, vec_n, 0, k, MPI_COMM_WORLD, &status ); MPI_Send( buf, n, vec_n, 0, k, MPI_COMM_WORLD );
}
}
}
tmin = tmin / 2.0; if (rank == 0)
printf("Struct\t%d\t%d\t%f\t%f\n",n,stride,tmin, n*sizeof(double)*1.0e-6 / tmin ); MPI_Type_free( &vec_n );
MPI_Finalize( ); return 0;
}
Задание 3.15.
/* пересылка вектора из 1000 элементов типа MPI_DOUBLE со страйдом 24 между элементами. Самостоятельная упаковка и распаковка (не используем типы данных MPI) */
#include <stdio.h> #include <stdlib.h> #include "mpi.h"
#define NUMBER_OF_TESTS 10 int main( argc, argv )
int argc; char **argv;
{ MPI_Datatype vec1, vec_n, old_types[2];
MPI_Aint |
indices[2]; |
|
double |
*buf, *lbuf, t1, t2, tmin; |
|
register double *in_p, *out_p; |
||
int |
i, j, k, nloop, rank, n, stride, blocklens[2]; |
|
MPI_Status |
status; |
|
|
|
288 |
MPI_Init( &argc, &argv );
MPI_Comm_rank( MPI_COMM_WORLD, &rank );
n = 1000; stride = 24; nloop = 100000/n;
buf = (double *) malloc( n * stride * sizeof(double) ); if (!buf)
{fprintf( stderr, "Could not allocate send/recv buffer of size %d\n",n * stride ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
lbuf = (double *) malloc( n * sizeof(double) ); if (!lbuf)
{fprintf( stderr, "Could not allocated send/recv lbuffer of size %d\n", n ); MPI_Abort( MPI_COMM_WORLD, 1 );
}
if (rank == 0) printf( "Kind\tn\tstride\ttime (sec)\tRate (MB/sec)\n" ); tmin = 1000;
for (k=0; k<NUMBER_OF_TESTS; k++)
{ if (rank == 0) { /* убедимся, что оба процесса готовы к приему/передаче */
|
MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 1, 14, |
|
|
MPI_BOTTOM, 0, MPI_INT, 1, 14, MPI_COMM_WORLD, &status ); |
|
|
t1 = MPI_Wtime(); |
|
for (j=0; j<nloop; j++) |
|
|
{ for (i=0; i<n; i++) |
/* создаем пользовательский тип */ |
|
|
lbuf[i] = buf[i*stride]; |
|
|
MPI_Send( lbuf, n, MPI_DOUBLE, 1, k, MPI_COMM_WORLD ); |
|
|
MPI_Recv( lbuf, n, MPI_DOUBLE, 1, k, MPI_COMM_WORLD, &status ); |
|
|
for (i=0; i<n; i++) |
buf[i*stride] = lbuf[i]; |
|
} |
|
|
t2 = (MPI_Wtime() - t1) / nloop; |
|
} |
if (t2 < tmin) tmin = t2; |
|
|
|
|
else |
|
|
if (rank == 1) |
|
|
{ |
/* убедимся, что оба процесса готовы к приему/передаче */ |
|
|
MPI_Sendrecv( MPI_BOTTOM, 0, MPI_INT, 0, 14, |
|
|
MPI_BOTTOM, 0, MPI_INT, 0, 14, MPI_COMM_WORLD, &status ); |
|
|
for (j=0; j<nloop; j++) |
|
|
{ MPI_Recv( lbuf, n, MPI_DOUBLE, 0, k, MPI_COMM_WORLD, &status ); |
|
|
for (i=0; i<n; i++) |
buf[i*stride] = lbuf[i]; |
|
for (i=0; i<n; i++) |
lbuf[i] = buf[i*stride]; |
MPI_Send( lbuf, n, MPI_DOUBLE, 0, k, MPI_COMM_WORLD );
}
}
}
tmin = tmin / 2.0;
289
if (rank == 0)
printf( "User\t%d\t%d\t%f\t%f\n",n,stride,tmin, n *sizeof(double)*1.0e-6 / tmin ); MPI_Finalize( );
return 0;
}
Задание 3.18.
#include <mpi.h> |
|
#include <memory.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <malloc.h> |
|
int main(int argc, char* argv[]) |
|
{ int iMyRank, i, gsize; |
|
MPI_Comm _Comm = MPI_COMM_WORLD; |
/* в группе все процессы */ |
MPI_Status status; |
|
double x[100][100][100], e[9][9][9]; |
|
int oneslice, twoslice, threeslice, sizeof_double; |
|
MPI_Init(&argc, &argv); |
|
MPI_Comm_size(_Comm, &gsize); |
|
MPI_Comm_rank(_Comm, &iMyRank); |
|
/* извлекаем секцию а(1-17-2, 3-11, 2-10) и записываем в е */ MPI_Type_extent(MPI_DOUBLE, &sizeof_double);
MPI_Type_vector(9, 1, 2, MPI_DOUBLE, &oneslice); MPI_Type_hvector(9, 1, 100 * sizeof_double, oneslice, &twoslice); MPI_Type_hvector(9, 1, 100 * 100 * sizeof_double, twoslice, &threeslice); MPI_Type_commit(&threeslice);
MPI_Sendrecv(&x[1][3][2], 1, threeslice, iMyRank, 0, e, 9*9*9, MPI_DOUBLE, iMyRank, 0, MPI_COMM_WORLD, &status);
if (iMyRank == 0) { printf("x = (");
for (i = 0; i < 100; ++i) printf("%f", x[i]); printf(")");
}
MPI_Finalize(); return 0;
}
Задание 3.19.
#include <mpi.h> #include <memory.h> #include <stdlib.h> #include <stdio.h> #include <malloc.h> #define MATRIXSIZE 10
int main(int argc, char* argv[])
290