Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 5.00/18: Рейтинг темы: голосов - 18, средняя оценка - 5.00
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
1

Распараллеливание вычисления интеграла используя редукции (OpenMP)

30.04.2018, 13:31. Показов 3670. Ответов 15

Доброго времени суток, ребята!

Ксть задача распараллелить процесс вычисления интеграла методом редукцией. В интернете прочитал, что редукция в данном случае это разбиение задачи на более простые задачи. Первое о чём подумал, это распаралелить по циклу for(...), который вычисляет значение функции в каждой точке, но это не работает так переменная в данном цикле должна быть типа int, но в этой задаче это невозможно, т.к. значение независимой переменной должна быть типа double.
Вот этот вариант:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
double integralReduction(double a, double b, double steps)
{
    int n = 3;
    double result = 0;
    double function = 0;
    double dx = (b - a) / steps;
    double startTime = 0, endTime = 0, time;
    omp_set_num_threads(n);
    double i;
#pragma omp parallel
    {
        startTime = omp_get_wtime();
#pragma omp for reduction (+:function)
            for (i = a; i <b; i += dx)
            {
                function += log(i) - (3 * sin(3 * i));
            }
            endTime = omp_get_wtime();
            printf("Time work: %f\nNumber potok: %d\n", endTime - startTime, omp_get_thread_num());
            result = function * dx;
    }
    return result;
}
Потом возник следующий вариант
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
double integralReduction(double a, double b, double steps)
{
    int n = 3;
    double result = 0;
    double function = 0;
    double dx = (b - a) / steps;
    double x = a;
    double startTime = 0, endTime = 0;
    omp_set_num_threads(n);
    int i;
#pragma omp parallel
    {
        startTime = omp_get_wtime();
#pragma omp for reduction (+:result,x) private (i)
            for (i = 0; i <steps; i++)
            {
                x += dx;
                function += log(x) - (3 * sin(3 * x));
                result += function * dx;
            }
            endTime = omp_get_wtime();
            printf("Time work: %f\nNumber potok: %d\n", endTime - startTime, omp_get_thread_num());
    }
    return result;
}
Но тут выдаёт следующею ошибку:
"Серьезность Код Описание Проект Файл Строка Состояние подавления
Ошибка C3017 неверный вид проверки завершения в операторе For директивы OpenMP Integral(OpenMP) d:\учеба фмит 2 курс\парал-ое программ-ие\integral(openmp)\integral(openmp)\integral(openmp).cpp 70
"
Подскажите пожалуйста в чём ошибка?
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
30.04.2018, 13:31
Ответы с готовыми решениями:

Распараллеливание с помощью OpenMP
Здравствуйте, уважаемые участники форума! Имеется цикл вида:for (i=1; i&lt;number; i++) { do...

OpenMP распараллеливание цикла
Привет кодеры! Нужна ваша помощь. У меня есть код который нужно распараллелить и тем самым получить...

Перевести программу с паскаля на фортран, используя Параллельные вычисления и OpenMP
Есть несложная программа на паскале, надо ее перевести в фортран с помощью параллельных вычислений...

OpenMP: распараллеливание цикла
Ниже представлена программа для численного интегрирования методом средних прямоугольников,...

15
Эксперт С++
1595 / 927 / 777
Регистрация: 06.02.2016
Сообщений: 2,412
Записей в блоге: 29
30.04.2018, 15:41 2
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <omp.h>
#include <cmath>
 
 
double integralReduction(double a, double b, double steps)
{
    int st=steps;
    int n = 3;
    double result = 0;
    double function = 0;
    double dx = (b - a) / steps;
    double x = a;
    double startTime = 0, endTime = 0;
    omp_set_num_threads(n);
    int i;
#pragma omp parallel
    {
        startTime = omp_get_wtime();
#pragma omp for reduction (+:result,x) private (i)
            for (i = 0; i <st; i++)
            {
                x += dx;
                function += log(x) - (3 * sin(3 * x));
                result += function * dx;
            }
            endTime = omp_get_wtime();
            printf("Time work: %f\nNumber potok: %d\n", endTime - startTime, omp_get_thread_num());
    }
    return result;
}
Добавлено через 4 минуты
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
циклу for(...),
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
.к. значение независимой переменной должна быть типа double.
Так не бывает

Добавлено через 16 секунд
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
циклу for(...),
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
.к. значение независимой переменной должна быть типа double.
Так не бывает
1
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
30.04.2018, 16:36  [ТС] 3
Цитата Сообщение от Peoples Посмотреть сообщение
Так не бывает
Поправил, сделал переменную steps int-ой и переместил private (i) в конец цикла for(...). Работает, но считает не верно (
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
30.04.2018, 18:03 4
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Работает, но считает не верно
ясное дело, что не правильно. проблема в x += dx. грубо говоря, все потоки вычисляют одни и те же значения x.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
double integralReduction(double a, double b, double steps)
{
    int st=steps;
    int n = 3;
    double result = 0;
    double function = 0;
    double dx = (b - a) / steps;    
    double startTime = 0, endTime = 0;
    omp_set_num_threads(n);
    int i;
#pragma omp parallel firstprivate(a, dx, function)
    {
        startTime = omp_get_wtime();
#pragma omp for reduction (+:result,x)
            for (i = 0; i < st; i++)
            {
                double x = a + i * dx;
                function += log(x) - (3 * sin(3 * x));
                result += function * dx;
            }
            endTime = omp_get_wtime();
            printf("Time work: %f\nNumber potok: %d\n", endTime - startTime, omp_get_thread_num());
    }
    return result;
}
private(i) можно не писать, счетчик цикла по умолчанию приватный
1
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
30.04.2018, 19:30  [ТС] 5
Цитата Сообщение от mat_for_c Посмотреть сообщение
ясное дело, что не правильно. проблема в x += dx. грубо говоря, все потоки вычисляют одни и те же значения x.
Исправил, но всё равно считает неверно
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
30.04.2018, 20:11 6
Лучший ответ Сообщение было отмечено Vlad__i__mir как решение

Решение

Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Исправил, но всё равно считает неверно
C++
1
2
function = log(x) - (3 * sin(3 * x)) 
result += function * dx;
для function не надо ничего складывать. просто вычисляем значение в точке

для проверки:
https://www.cyberforum.ru/cgi-bin/latex.cgi?\int_{1}^{10}f(x)dx = 15.17
0
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
02.05.2018, 21:10  [ТС] 7
Цитата Сообщение от mat_for_c Посмотреть сообщение
для function не надо ничего складывать. просто вычисляем значение в точке
Спасибо!

У меня возник вопрос почему это так важно?
Ведь вот здесь мы указали, что для каждого потока мы создаём свою копию переменной function инициализированной 0-ом:

Цитата Сообщение от mat_for_c Посмотреть сообщение
#pragma omp parallel firstprivate(a, dx, function)
Поправьте пожалуйста меня, если я не верно понял.

А вот здесь мы говорим, что для каждой итерации цикла for() мы создаём отдельный поток:
Цитата Сообщение от mat_for_c Посмотреть сообщение
#pragma omp for reduction (+:result,x)
Кажется, что для каждого i цикла for() (потока) должна быть одна единственная переменная function=0, и она вычисляется единожды (поток ведь для каждой i) в этом потоке, далее при выходе из потока она не суммируется.

Не пойму как тогда это "function+=" может суммироваться?
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
02.05.2018, 21:26 8
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Не пойму как тогда это "function+=" может суммироваться
ок, рассмотрим ситуацию без распараллеливания (т.е. 1 главный поток) function += f(x) f(x) - для сокращения.

в начальный момент func = 0; все ок пока, что пусть шаг dx = 1; для простоты.
итого имеем:

_f(1) = 0 + f(1)
_f(2) = _f(1) + f(2) = f(1) + f(2);
_f(3) = _f(2) + f(3) = f(1) + f(2) + f(3);
_f(4) = _f(3) + f(4) = ... = f(1) + f(2) + f(3) + f(4);

S = (_f(1) + _f(2) + _f(3) + _f(4)) * dx = (4 * f(1) + 3 * f(2) + 2 * f(3) + f(4)) * dx;


а нам надо просто подсчитать f(x) и умножить его на dx, т.е. в итоге получить следующее:
S = (f(1) + f(2) + f(3) + f(4)) * dx Чувствуете разницу? У вас была ошибка не при распараллеливании, а в самом алгоритме.

Добавлено через 3 минуты
_f(x) - это ваша переменная function
0
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
02.05.2018, 21:50  [ТС] 9
Но ведь в случае распараллеливания по циклу for() разве должно быть не вот так:
0-ой поток:
i=0
_f(0) = 0 + f(0)
S0 = f(0) * dx

1-ый поток:
i=1
_f(1) = 0 + f(1)
S1 = f(1) * dx

...
n-ый поток:
i=n
_f(n) = 0 + f(т)
Sn = f(n) * dx
---------
S=S0+S1+...+Sn
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
02.05.2018, 22:04 10
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Но ведь в случае распараллеливания по циклу for() разве должно быть не вот так:
Это только начало. Поток же дальше работает. По умолчанию OpenMP разбивает число итераций между потоками одинаково.
Пусть будет 8 итераций на 2 потока. Тогда имеем:

1 поток:
_f(1) = 0 + f(1);
_f(2) = _f(1) + f(2);
_f(3) = _f(2) + f(3);
_f(4) = _f(3) + f(4) = 4 * f(1) + 3 * f(2) + 2 * f(3) + f(4);
result = [4 * f(1) + 3 * f(2) + 2 * f(3) + f(4)] * dx;


2 поток:
_f(1) = 0 + f(5);
_f(2) = _f(1) + f(6);
_f(3) = _f(2) + f(7);
_f(4) = _f(3) + f(8) = 4 * f(5) + 3 * f(6) + 2 * f(7) + f(8);
result = [4 * f(5) + 3 * f(6) + 2 * f(7) + f(8)] * dx;


Теперь делаем редукцию result и получаем:

[4*f(1)+3*f(2)+2*f(3)+f(4) + 4*f(5)+3*f(6)+2*f(7)+f(8)] * dx что опять не равно
[f(1)+f(2)+f(3)+f(4) + f(5)+f(6)+f(7)+f(8)]*dx

Добавлено через 3 минуты
P.S. [] - это не целая часть числа, а обычные скобки
1
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
02.05.2018, 22:14  [ТС] 11
Цитата Сообщение от mat_for_c Посмотреть сообщение
По умолчанию OpenMP разбивает число итераций между потоками одинаково.
Я понял, в одном потоке может быть несколько итераций цикла for(), а не одна итерация - один поток. Спасибо!
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
02.05.2018, 22:29 12
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
а не одна итерация - один поток
для такого случая должна подойти опция schedule (dynamic) возле omp for

Добавлено через 3 минуты
Цитата Сообщение от mat_for_c Посмотреть сообщение
должна подойти
Но не подойдет

Добавлено через 3 минуты
она просто позволит распределять итераций по одной на поток, но это ничего не изменит.
0
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
02.05.2018, 22:41  [ТС] 13
Цитата Сообщение от mat_for_c Посмотреть сообщение
она просто позволит распределять итераций по одной на поток, но это ничего не изменит.
Почему? Если на каждую итерацию будет выделен отдельный поток со своей переменной?
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
02.05.2018, 22:48 14
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Если на каждую итерацию будет выделен отдельный поток со своей переменной
вот именно, на первой итерации будет то, что нужно. а потом вернемся к предыдущему случаю, который я расписывал, только там не четко все будет, а в разброс, наподобие этого:

1 thread:

_f(1) = 0 + f(1);
_f(2) = _f(1) + f(2);
_f(3) = _f(2) + f(5);
_f(7) = _f(3) + f(7);

S = 4*f(1) + 3*f(2) + 2* f(5) + f(7);


2 thread
_f(1) = 0 + f(3);
_f(2) = _f(1) + f(4);
_f(3) = _f(2) + f(6);
_f(4) = _f(3) + f(8);

S = 4*f(3) + 3*f(4) + 2* f(6) + f(8);


а нам надо известно что
0
6 / 6 / 1
Регистрация: 04.01.2017
Сообщений: 465
02.05.2018, 23:06  [ТС] 15
Цитата Сообщение от mat_for_c Посмотреть сообщение
вот именно, на первой итерации будет то, что нужно. а потом вернемся к предыдущему случаю, который я расписывал, только там не четко все будет, а в разброс, наподобие этого:
Т.е., создастся количество потоков равное количеству итераций цикла for(), но при этом openmp всё равно может отдать одному потоку несколько итераций, а другому ничего?
0
223 / 213 / 80
Регистрация: 26.04.2013
Сообщений: 972
02.05.2018, 23:40 16
Цитата Сообщение от Vlad__i__mir Посмотреть сообщение
Т.е., создастся количество потоков равное количеству итераций цикла for()
Нет, создастся столько потоков, сколько вы укажете в omp_set_num_threads(n); Просто потоки по очереди будут разбирать итерации.
Это как 2 грузчика, вагон мешков картошки и 1 подставка, с которой они берут этот мешок. Кто первый освободится, тот и пойдет за очередным мешком.
Т.е. ваше желание использовать func += f(x) удовлетворит только omp_set_num_threads(N);, N - число итераций, но я не советую создавать потоков куда больше, чем число ядер процессора.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
02.05.2018, 23:40

Помощь в написании контрольных, курсовых и дипломных работ здесь.

OpenMP распараллеливание вычислений
Выручите, перепробовал уже с бубном плясать не выходит. Матрицу создаю рандомно, элементов должно...

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

Распараллеливание цикла For с использованием OpenMP
Всем привет. Задался целью изучить OpenMP, что бы в дальнейшем уметь распараллеливать программы....

Распараллеливание циклов с использованием OpenMP C++
Доброго времени суток. (Нужен совет, так как разбираюсь с omp почти 3 дня и не хватает знанний) ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.