Переменные среды и вспомогательные функции




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

void omp_set_num_threads(int num)

Следующий пример демонстрирует применение функции omp_set_num_threads() и опции num_threads. Перед первой параллельной областью вызовом функцииomp_set_num_threads(2) выставляется количество нитей, равное 2. Но к первой параллельной области применяется опция num_threads(3), которая указывает, что данную область следует выполнять тремя нитями. Следовательно, сообщение "Параллельная область 1"будет выведено тремя нитями. Ко второй параллельной области опция num_threads не применяется, поэтому действует значение, установленное функцией omp_set_num_threads(2), и сообщение "Параллельная область 2"будет выведено двумя нитями.

#include <stdio.h>
#include <omp.h>
int main(int argc, char *argv[])
{
omp_set_num_threads(2);
#pragma omp parallel num_threads(3)
{
printf("Параллельная область 1\n");
}
#pragma omp parallel
{
printf("Параллельная область 2\n");
}
}

В некоторых случаях система может динамически изменять количество нитей, используемых для выполнения параллельной области, например, для оптимизации использования ресурсов системы. Это разрешено делать, если переменная среды OMP_DYNAMIC установлена в true. В системах с динамическим изменением количества нитей значение по умолчанию не определено, иначе значение по умолчанию: false. Переменную OMP_DYNAMIC можно установить с помощью функции omp_set_dynamic().

void omp_set_dynamic(int num)

В качестве значения параметра функции omp_set_dynamic() задаётся 0 или 1. Если система не поддерживает динамическое изменение количества нитей, то при вызове функции omp_set_dynamic() значение переменной OMP_DYNAMIC не изменится. Узнать значение переменной OMP_DYNAMIC можно при помощи функции omp_get_dynamic().

int omp_get_dynamic(void)

Следующий пример демонстрирует применение функций omp_set_dynamic() и omp_get_dynamic(). Сначала распечатывается значение, полученное функциейomp_get_dynamic() – это позволяет узнать значение переменной OMP_DYNAMIC по умолчанию. Затем при помощи функции omp_set_dynamic() переменная OMP_DYNAMICустанавливается в true, что подтверждает выдача ещё один раз значения функции omp_get_dynamic(). Затем порождается параллельная область, выполняемая заданным количеством нитей (128). В параллельной области печатается реальное число выполняющих её нитей. Директива master позволяет обеспечить печать только процессом-мастером. В системах с динамическим изменением числа нитей выданное значение может отличаться от заданного (128).

#include <stdio.h>
#include <omp.h>
int main(int argc, char *argv[])
{
printf("Значение OMP_DYNAMIC: %d\n", omp_get_dynamic());
omp_set_dynamic(1);
printf("Значение OMP_DYNAMIC: %d\n", omp_get_dynamic());
#pragma omp parallel num_threads(128)
{
#pragma omp master
{
printf("Параллельная область, %d нитей\n",
omp_get_num_threads());
}
}
}

Функция omp_get_max_threads() возвращает максимально допустимое число нитей для использования в следующей параллельной области.

int omp_get_max_threads(void)

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

int omp_get_num_procs(void)

Параллельные области могут быть вложенными; по умолчанию вложенная параллельная область выполняется одной нитью. Это управляется установкой переменной среды OMP_NESTED. Изменить значение переменной OMP_NESTED можно с помощью вызова функции omp_set_nested().

void omp_set_nested(int nested)

Функция omp_set_nested() разрешает или запрещает вложенный параллелизм. В качестве значения параметра задаётся 0 или 1.Если вложенный параллелизм разрешён, то каждая нить, в которой встретится описание параллельной области, породит для её выполнения новую группу нитей. Сама породившая нить станет в новой группе нитью-мастером. Если система не поддерживает вложенный параллелизм, данная функция не будет иметь эффекта.

Следующий пример демонстрирует использование вложенных параллельных областей и функции omp_set_nested(). Вызов функции omp_set_nested() перед первой частью разрешает использование вложенных параллельных областей. Для определения номера нити в текущей параллельной секции используются вызовы функции omp_get_thread_num(). Каждая нить внешней параллельной области породит новые нити, каждая из которых напечатает свой номер вместе с номером породившей нити. Далее вызов omp_set_nested() запрещает использование вложенных параллельных областей. Во второй части вложенная параллельная область будет выполняться без порождения новых нитей, что и видно по получаемой выдаче.

#include <stdio.h>
#include <omp.h>
int main(int argc, char *argv[])
{
int n;
omp_set_nested(1);
#pragma omp parallel private(n)
{
n=omp_get_thread_num();
#pragma omp parallel
{
printf("Часть 1, нить %d - %d\n", n,
omp_get_thread_num());
}
}
omp_set_nested(0);
#pragma omp parallel private(n)
{
n=omp_get_thread_num();
#pragma omp parallel
{
printf("Часть 2, нить %d - %d\n", n,
omp_get_thread_num());
}
}
}

Узнать значение переменной OMP_NESTED можно при помощи функции omp_get_nested().

int omp_get_nested(void)

Функция omp_in_parallel() возвращает 1, если она была вызвана из активной параллельной области программы.

int omp_in_parallel(void)

Следующий пример иллюстрирует применение функции omp_in_parallel(). Функция mode демонстрирует изменение функциональности в зависимости от того, вызвана она из последовательной или из параллельной области. В последовательной области будет напечатано "Последовательная область а в параллельной – "Параллельная область".

#include <stdio.h>
#include <omp.h>
void mode(void)
{
if(omp_in_parallel()) printf("Параллельная область\n");
else printf("Последовательная область\n");
}
int main(int argc, char *argv[])
{
mode();
#pragma omp parallel
{
#pragma omp master
{
mode();
}
}
}

Измерение времени

Возвращает затраченное время в секундах:

double omp_get_wtime(void);

Возвращает точность таймера, используемого предыдущей описанной функцией:

double omp_get_wtick(void);

 

Примеры программ

Для того, чтобы окончательно уяснить для себя принципы работы с OpenMP, далее приведен простой, но достаточно емкий пример параллельного вычисления числа пи с приминением OpenMP:

#include <stdio.h>
#include <math.h>
#include <omp.h>

int main()
{
const int N = 10000000;
const double L = 1.0;
const double h = L / N;
const double x_0 = 0.0;

double pi;
double t_1, t_2; //временные интервалы

int i;
double sum = 0.0;

t_1 = omp_get_wtime(); //начальный замер времени

Инициализируем параллельную область для основного блока вычислений, используя директиву parallel for с опциями reduction и schedule:

#pragma omp parallel for reduction(+: sum) schedule(static)
for (i = 0; i < N; ++i)
{
double x = x_0 + i * h + h/2;
sum += sqrt(1 - x*x);
}

Делаем конечный замер времени и выводим результаты на экран:

t_2 = omp_get_wtime();

pi = sum * h * 4.0;

printf("omp_get_max_threads(): %d\n", omp_get_max_threads());
printf("time: %f\n", t_2 - t_1);
printf("pi ~ %f\n", pi);

return 0;
}

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

Задаем три матрицы размерностью 4096×4096 элементов каждая, первые две инициализируются, в третью записывается результат полученных вычислений.

#include <stdio.h>
#include <omp.h>

#define N 4096

double a[N][N], b[N][N], c[N][N];

int main()
{
int i, j, k;
double t1, t2; //временные интервалы

Инициализируем матрицу, замеряем время начала работы основного вычислительного блока:

for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
a[i][j] = b[i][j] = i * j;

t1=omp_get_wtime();

Основной вычислительный блок, массивы a, b и c задаются общими, а переменные i, j и k частными. После окончания вычислений проводится очередной замер времени после которого выводится общее время выполнения программы:

#pragma omp parallel for shared(a, b, c) private(i, j, k)
for(i = 0; i < N; i++){
for(j = 0; j < N; j++){
c[i][j] = 0.0;
for(k = 0; k < N; k++) c[i][j] += a[i][k] * b[k][j];
}
}

t2=omp_get_wtime();

printf("Time=%lf\n", t2-t1);
}

Задания

Задание 1.

Напишите параллельную программу, реализующую скалярное произведение двух векторов.

Задание 2.

Напишите параллельную программу, реализующую поиск максимального значения вектора.

Задание 3.

Напишите параллельную программу, реализующую транспонирование матрицы n×n.

Задание 4.

Реализуйте параллельный алгоритм метода Гаусса решения систем линейных алгебраических уравнений.

Задание 5.

На основе программы для решения СЛАУ методом Гаусса напишите параллельную программу для вычисления определителя и постройте зависимость коэффициента ускорения от числа ядер (потоков) на узле.

Задание 6.

На основе программы для решения СЛАУ методом Гаусса напишите параллельную программу для вычисления обратной матрицы и постройте зависимость коэффициента ускорения от числа ядер (потоков) на узле.

Задание 7.

Напишите параллельную программу реализовывающую метод Гаусса (метод элементарных преобразований) для вычисления ранга произвольной матрицы A – m×n.

Задание 8.

Найти численное решение задачи Дирихле для уравнения Пуассона итерационным методом Зейделя.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-01-08 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: