Отчет по практическому заданию Разработка

advertisement
МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
ИМЕНИ М. В. ЛОМОНОСОВА
ФАКУЛЬТЕТ ВЫЧИСЛИТЕЛЬНОЙ МАТЕМАТИКИ И КИБЕРНЕТИКИ
Отчет по практическому заданию
«Разработка параллельной программы и
исследование ее эффективности»
Выполнил студент Туренко А. С.
Преподаватель Кузина Л. Н.
Москва, 2014
Содержание
1 Постановка задачи
2
2 Результаты
2
3 Исходный код
2
4 Выводы
6
1
Постановка задачи
Дана последовательная программа, реализующая алгоритм релаксации Якоби. Требуется разработать параллельную программу с использованием технологии OpenMP и провести исследование
ее эффективности. В частности, исследовать время выполнения разработанной программы в зависимости от размера сетки и количества используемых потоков на вычислительном комплексе IBM
Regatta.
2
Результаты
Программа компилировалась со следующими параметрами (пример для сетки 256x256x256):
$ gcc -Wall -Wextra -std=c99 -pedantic -O2 -DN=256 -fopenmp -o lab1_1-256 lab1_1.c
Для последовательной версии (без использования OpenMP):
$ gcc -Wall -Wextra -std=c99 -pedantic -O2 -DN=256 -o lab1_1-256-noomp lab1_1.c
Для сеток 128x128x128 и 384x384x384 параметры аналогичны.
Результаты:
Размер
сетки
Последовательный
алгоритм, сек
128x128x128
256x256x256
384x384x384
10,8669
109,3690
331,6430
1 процессор
Время
Уск.
11,5534
0,9406
111,6110
0,9799
349,1800
0,9498
Параллельный
2 процессора
Время
Уск.
5,7421
1,8925
58,4983
1,8696
183,1470
1,8108
алгоритм, сек
4 процессора
Время
Уск.
3,2603
3,3331
34,8567
3,1377
104,7010
3,1675
1 процессор
Время
Уск.
10,0709
0,8314
104,3670
0,8732
313,2430
1,0033
Параллельный алгоритм, сек
2 процессора
4 процессора
Время
Уск.
Время
Уск.
4,8606
1,7226
2,6187
3,1973
51,9095
1,7555
27,0217
3,3724
154,0950
2,0394
81,4303
3,8593
8 процессоров
Время
Уск.
2,3134
4,6974
20,9346
5,2243
67,3500
4,9242
Для версии с -O3 вместо -O2:
Размер
сетки
Последовательный
алгоритм, сек
128x128x128
256x256x256
384x384x384
8,3730
91,1276
314,2640
8 процессоров
Время
Уск.
1,3989
5,9855
13,5873
6,7068
44,7457
7,0233
Графики зависимости ускорения от количества процессоров по данным из этих таблиц (для O2 и -O3) приведены ниже на отдельной странице. Красным цветом обозначен график для сетки
128x128x128, зеленым — 256x256x256, черным — 384x384x384. График y=x показывает максимально
достижимое в теории ускорение (при отсутствии накладных расходов на распараллеливание).
3
Исходный код
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef _OPENMP
#include <omp.h>
#else
#include <sys/time.h>
#endif
#define MAX(a,b) ((a)>(b)?(a):(b))
#ifndef N
#define N 256
#endif
#define NN ((N)*(N))
#define NNN ((NN)*(N))
#ifndef MAX_THREADS_CNT
#define MAX_THREADS_CNT 8
#endif
static const double maxeps = 0.1e-7;
8
Speedup
6
4
2
0
0
2
4
CPUs
6
8
Рис. 1: График зависимости укорения от количества процессоров для -O2
8
Speedup
6
4
2
0
0
2
4
CPUs
6
8
Рис. 2: График зависимости укорения от количества процессоров для -O3
static const int itmax = 100;
double eps;
double data1[NNN], data2[NNN];
double *A, *B;
double m[MAX_THREADS_CNT];
void
void
void
void
void
relax();
resid();
init();
verify();
wtime(double *t);
int main()
{
double time0, time1;
/* omp_set_num_threads(MAX_THREADS_CNT); */
A = data1;
B = data2;
init();
wtime(&time0);
for (int it = 1; it <= itmax; ++it)
{
eps = 0.0;
relax();
resid();
printf("it=%4i eps=%f\n", it, eps);
if (eps < maxeps)
break;
}
wtime(&time1);
printf("Time in seconds=%gs\t", time1 - time0);
verify();
return 0;
}
void init()
{
for (int i = 0; i <= N-1; ++i)
for (int j = 0; j <= N-1; ++j)
for (int k = 0; k <= N-1; ++k)
if (i == 0 || i == N-1 ||
j == 0 || j == N-1 ||
k == 0 || k == N-1)
{
A[i*NN + j*N + k] = 0.0;
} else {
A[i*NN + j*N + k] = (4.0 + i + j + k);
}
}
void relax()
{
#pragma omp parallel for schedule(dynamic)
for (int i = 1; i <= N-2; ++i)
for (int j = 1; j <= N-2; ++j)
for (int k = 1; k <= N-2; ++k)
{
int idx = i*NN + j*N + k;
B[idx] = (
A[idx - NN] +
A[idx + NN] +
A[idx
A[idx
A[idx
A[idx
+
+
N
N
1
1
] +
] +
] +
]) / 6.0;
}
}
#ifndef _OPENMP
void resid()
{
double *T;
for (int i = 1; i <= N-2; ++i)
for (int j = 1; j <= N-2; ++j)
for (int k = 1; k <= N-2; ++k)
{
int idx = i*NN + j*N + k;
double e = fabs(A[idx] - B[idx]);
eps = MAX(eps, e);
}
T = A;
A = B;
B = T;
}
#else
void resid()
{
double *T;
for (int t = 0; t < MAX_THREADS_CNT; ++t)
m[t] = 0.0;
#pragma omp parallel for schedule(static)
for (int i = 1; i <= N-2; ++i)
for (int j = 1; j <= N-2; ++j)
for (int k = 1; k <= N-2; ++k)
{
int t = omp_get_thread_num();
int idx = i*NN + j*N + k;
double e = fabs(A[idx] - B[idx]);
m[t] = MAX(m[t], e);
}
for (int t = 0; t < MAX_THREADS_CNT; ++t) {
eps = MAX(eps, m[t]);
}
T = A;
A = B;
B = T;
}
#endif
void verify()
{
double s;
s = 0.0;
for (int i = 0; i <= N-1; ++i)
for (int j = 0; j <= N-1; ++j)
for (int k = 0; k <= N-1; ++k)
s = s + A[i*NN + j*N + k] * (i+1) * (j+1) * (k+1) / (NNN);
printf(" S = %f\n", s);
}
#ifndef _OPENMP
void wtime(double *t)
{
static int sec = -1;
struct timeval tv;
gettimeofday(&tv, (void *)0);
if (sec < 0)
sec = tv.tv_sec;
*t = (tv.tv_sec - sec) + 1.0e-6 * tv.tv_usec;
}
#else
void wtime(double *t)
{
*t = omp_get_wtime();
}
#endif
4
Выводы
Видно, что скомпилированная с поддержкой OpenMP программа, запускаемая на одном процессоре, в основном проигрывает последовательной. Это объясняется различием в вычислении максимума в параллельной и последовательной версии функции resid(): параллельная версия вычисляет
M максимумов, где M — число потоков, вычисляя затем максимум среди них, что ведет к дополнительным накладным расходам.
Ускорение зависит от количества процессоров не совсем пропорционально, что, учитывая накладные расходы на поддержание параллелизма и синхронизацию, и ожидалось. Ускорение увеличивается на величину, немного меньшую двойки, при удвоении числа процессоров и неизменности
параметров компиляции и алгоритма.
Download