-
Notifications
You must be signed in to change notification settings - Fork 80
Еремин Василий. Технология SEQ-MPI. Интегрирование – метод прямоугольников. Вариант 19 #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d9f0f06
93ca59a
7ad97bb
84dcbe5
9b5fe3b
f88e0b1
c53bbab
dc356c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| #pragma once | ||
|
|
||
| #include <functional> | ||
| #include <tuple> | ||
|
|
||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace eremin_v_rectangle_method { | ||
|
|
||
| using InType = std::tuple<double, double, int, std::function<double(double)>>; | ||
| using OutType = double; | ||
| using TestType = std::tuple<int, double, double, int, std::function<double(double)>, std::function<double(double)>>; | ||
| using BaseTask = ppc::task::Task<InType, OutType>; | ||
|
|
||
| } // namespace eremin_v_rectangle_method | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "student": { | ||
| "first_name": "Василий", | ||
| "last_name": "Еремин", | ||
| "middle_name": "Евгеньевич", | ||
| "group_number": "3823Б1ФИ2", | ||
| "task_number": "1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "eremin_v_rectangle_method/common/include/common.hpp" | ||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace eremin_v_rectangle_method { | ||
|
|
||
| class EreminVRectangleMethodMPI : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kMPI; | ||
| } | ||
| explicit EreminVRectangleMethodMPI(const InType &in); | ||
|
|
||
| private: | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace eremin_v_rectangle_method |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| #include "eremin_v_rectangle_method/mpi/include/ops_mpi.hpp" | ||
|
|
||
| #include <mpi.h> | ||
|
|
||
| #include <cmath> | ||
| #include <tuple> | ||
|
|
||
| #include "eremin_v_rectangle_method/common/include/common.hpp" | ||
|
|
||
| namespace eremin_v_rectangle_method { | ||
|
|
||
|
allnes marked this conversation as resolved.
|
||
| EreminVRectangleMethodMPI::EreminVRectangleMethodMPI(const InType &in) { | ||
| SetTypeOfTask(GetStaticTypeOfTask()); | ||
| GetInput() = in; | ||
| GetOutput() = 0; | ||
|
allnes marked this conversation as resolved.
|
||
| } | ||
|
|
||
| bool EreminVRectangleMethodMPI::ValidationImpl() { | ||
| auto &input = GetInput(); | ||
| return (std::get<0>(input) < std::get<1>(input)) && (std::get<2>(input) > 0) && (std::get<2>(input) <= 100000000) && | ||
| (std::get<0>(input) >= -1e9) && (std::get<0>(input) <= 1e9) && (std::get<1>(input) >= -1e9) && | ||
| (std::get<1>(input) <= 1e9) && (GetOutput() == 0); | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodMPI::PreProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodMPI::RunImpl() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The data is not being scattered from rank 0 to other ranks
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Но у меня же есть: MPI_Bcast(&lower_bound, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast(&upper_bound, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Bcast(&steps, 1, MPI_INT, 0, MPI_COMM_WORLD);
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not scattered though (split by fragments and send partially). I'm ok to accept as is |
||
| int rank = 0; | ||
|
allnes marked this conversation as resolved.
|
||
| int size = 0; | ||
| MPI_Comm_rank(MPI_COMM_WORLD, &rank); | ||
| MPI_Comm_size(MPI_COMM_WORLD, &size); | ||
|
|
||
| double lower_bound = 0.0; | ||
| double upper_bound = 0.0; | ||
| int steps = 0; | ||
| double result = 0.0; | ||
|
|
||
| if (rank == 0) { | ||
| auto &input = GetInput(); | ||
| lower_bound = std::get<0>(input); | ||
| upper_bound = std::get<1>(input); | ||
| steps = std::get<2>(input); | ||
| } | ||
| MPI_Bcast(&lower_bound, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); | ||
| MPI_Bcast(&upper_bound, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); | ||
| MPI_Bcast(&steps, 1, MPI_INT, 0, MPI_COMM_WORLD); | ||
|
|
||
| const auto in_function = std::get<3>(GetInput()); | ||
|
|
||
| double step_size = (upper_bound - lower_bound) / static_cast<double>(steps); | ||
| double local_result = 0.0; | ||
|
|
||
| for (int i = rank; i < steps; i += size) { | ||
| local_result += in_function(lower_bound + ((static_cast<double>(i) + 0.5) * step_size)); | ||
| } | ||
|
|
||
| local_result *= step_size; | ||
|
|
||
| MPI_Allreduce(&local_result, &result, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); | ||
| GetOutput() = result; | ||
| return true; | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodMPI::PostProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace eremin_v_rectangle_method | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| # Интегрирование – метод прямоугольников | ||
|
|
||
| - **Студент:** Еремин Василий Егвеньевич, группа 3823Б1ФИ2 | ||
| - **Технология:** SEQ | MPI | ||
| - **Вариант:** №19 | ||
|
|
||
| ## 1. Введение | ||
|
|
||
| **Цель работы:** Реализация и сравнительный анализ последовательной и параллельной версий алгоритма численного интегрирования методом прямоугольников. | ||
|
|
||
| **Задачи:** | ||
| 1. Реализовать последовательную версию алгоритма метода прямоугольников | ||
| 2. Реализовать параллельную версию с использованием технологии MPI | ||
| 3. Провести сравнительный анализ производительности и эффективности обеих реализаций | ||
|
|
||
| ## 2. Постановка задачи | ||
|
|
||
| **Задача**: Требуется вычислить значение определенного интеграла методом прямоугольников | ||
|
|
||
|  | ||
|
|
||
| где: | ||
| - `f(x)` — интегрируемая функция | ||
| - `a`, `b` — нижний и верхний пределы интегрирования соответственно | ||
|
|
||
| **Описание метода решения:** Из трех методов (левых, правых и средних прямоугольников) был выбран *метод средних прямоугольников*. | ||
|
|
||
| Область интегрирования разбивается на `n` равных отрезков. Сложная площадь под кривой разбивается на множество простых прямоугольников - на каждом отрезке строится прямоугольник высотой, равной значению функции в середине отрезка. Общая площадь вычисляется как сумма площадей всех этих прямоугольников. | ||
|
|
||
|  | ||
|
|
||
| **Входные данные:** | ||
| - `a` — нижний предел интегрирования (вещественное число) | ||
| - `b` — верхний предел интегрирования (вещественное число) | ||
| - `n` — количество отрезков разбиения (целое число) | ||
| - `f(x)` — интегрируемая функция | ||
|
|
||
| **Выходные данные:** | ||
| - Приближенное значение интеграла (вещественное число) | ||
|
|
||
| **Ограничения:** | ||
| - `-1 000 000 000 ≤ a < b ≤ 1 000 000 000` | ||
| - `0 < n ≤ 100 000 000` | ||
|
|
||
|
|
||
| ## 3. Описание алгоритма (последовательного) | ||
| **Алгоритм последовательного вычисления:** | ||
| 1. **Найдем шаг интегрирования:** `h = (b - a) / n` | ||
| 2. **Вычисляем сумму значений функции:** через цикл находим площадь каждого прямоугольника по формуле `f(a + (i + 0.5) * h)` | ||
| 3. **Умножаем сумму на шаг:** `result = h * sum` | ||
|
|
||
| **Реализация на C++:** | ||
|
|
||
| ```cpp | ||
| double rectangle_method(double a, double b, double n) { | ||
| double result = 0.0; | ||
| double h = (b - a) / n; | ||
|
|
||
| for (int i = 0; i < n; i++) { | ||
| result += f(a + ((i + 0.5) * h)); | ||
| } | ||
|
|
||
| result *= h; | ||
| return result; | ||
| } | ||
| ``` | ||
|
|
||
| ## 4. Схема распараллеливания (MPI) | ||
| **Алгоритм параллельного вычисления:** | ||
| Все тоже самое, но | ||
| 1. **Рассылка праметров:** Процесс с рангом 0 получает и рассылает параметры `a`, `b`, `n` всем процессам для правильной синхронизации. | ||
| 2. **Каждый процесс вычисляет шаг интегрирования:** `h = (b - a) / n` | ||
| 3. **Локальное вычисление:** Каждый процесс независимо вычисляет свою частичную сумму по формуле `f(a + (i + 0.5) * h)` | ||
| 4. **Сбор результатов:** Все процессы обмениваются результатами и получают общую сумму | ||
| 5. **Финальный расчет:** `result = h * sum` | ||
|
|
||
| **Принцип разделения отрезков:** | ||
|
|
||
| ```cpp | ||
| for (int i = rank; i < n; i += size) { | ||
| local_result += f(a + ((i + 0.5) * step_size)); | ||
| } | ||
| ``` | ||
|
|
||
| **Схема распределения вычислений:** | ||
| - Процесс 0: обрабатывает отрезки 0, size, 2×size, ... | ||
| - Процесс 1: обрабатывает отрезки 1, size+1, 2×size+1, ... | ||
| - Процесс k: обрабатывает отрезки k, size+k, 2×size+k, ... | ||
|
|
||
| ## 5. Детали реализации | ||
| **Дополнительные функции в тестах:** | ||
| - `InFunction(x)` - подынтегральная функция (что интегрируем) | ||
| - `Function(x)` - первообразная (аналитическое решение для проверки) | ||
|
|
||
| ## 6. Экспериментальная среда | ||
| - Hardware/OS: Apple M1 Pro, 6 ядер производительности и 2 эффективности, 16 ГБ, Ubuntu 24.04.2 (DevContainer) | ||
| - Toolchain: GCC 13.3.0, C++20, CMake 3.28.3, build type Release | ||
| - Environment: PPC_NUM_PROC = 8 | ||
|
|
||
| ## 7. Результаты и обсуждение | ||
|
|
||
| ### 7.1 Проверка корректности | ||
|
|
||
| **Методы верификации корректности:** | ||
|
|
||
| 1. **Сравнение с аналитическим решением:** | ||
| - Используется функция `Function(x)` - первообразная от `InFunction(x)` | ||
| - Ожидаемый результат вычисляется по формуле Ньютона-Лейбница: `Function(b) - Function(a)` | ||
| - Позволяет получить эталонное значение для сравнения | ||
|
|
||
| 2. **Автоматизированное тестирование:** | ||
| - Реализованы юнит-тесты для 5 различных тестовых случаев | ||
| - Тестируются разные интервалы и количество разбиений | ||
| - Проверяются как последовательная, так и MPI версии | ||
|
|
||
| 3. **Допустимая погрешность:** | ||
| ```cpp | ||
| double tolerance = std::max(std::abs(expected_result_) * 0.01, 1e-8); | ||
| ``` | ||
| - Относительная погрешность: 1% | ||
| - Абсолютная погрешность: не менее 1e-8 | ||
| - Учитывает масштаб ожидаемого результата | ||
|
|
||
| **Тестовые случаи:** | ||
| - **Разные интервалы:** [0.0, 1.0], [0.0, 2.0], [-10.0, 100.0], [1.0, 4.0], [-2.0, 3.0] | ||
| - **Разное количество разбиений:** от 1,000 до 100,000 | ||
| - **Разные тестовые функции:** | ||
| - `x^2` | ||
| - `sin(x)` | ||
| - `e^x` | ||
| - `x * e^x` | ||
| - `x^3 - 4x + 1` | ||
| - `e^x * (sin(x) + cos(x)) + 3x^2 * cos(x) - x^3 * sin(x)` | ||
|
|
||
| **Результат:** Все тесты проходят успешно, что подтверждает корректность обеих реализаций. | ||
|
|
||
| ### 7.2 Производительность | ||
| - **Сложная тестовая функция:** `e^x * (x^2 * sin(x) + 2x * sin(x) + x^2 * cos(x)) + 4x^3 * cos(2x) - 2x^4 * sin(2x)` | ||
| - **Количество разбиений** `100 000 000` | ||
|
|
||
| **Полученные результаты:** | ||
|
|
||
| | **Режим** | **Количество процессов** | **Время, с** | **Speedup** | **Efficiency** | | ||
| |-----------|--------------------------|--------------|-------------|----------------| | ||
| | SEQ | 1 | 2.182 | 1.00 | N/A | | ||
| | MPI | 2 | 1.049 | 2.08 | 104% | | ||
| | MPI | 4 | 0.566 | 3.92 | 98% | | ||
| | MPI | 6 | 0.378 | 5.77 | 96% | | ||
|
|
||
|
|
||
| ## 8. Заключение | ||
| В рамках данной работы были успешно реализованы последовательная и параллельная версии алгоритма интегрирования методом прямоугольников. Проведенные эксперименты подтвердили значительное ускорение MPI-реализации и корректность работы обоих вариантов алгоритма. | ||
|
|
||
| ## 9. Источники | ||
| 1. [Wikipedia](https://ru.wikipedia.org/wiki/Метод_прямоугольников) | ||
| 2. [Презентация по курсу](https://learning-process.github.io/parallel_programming_slides/slides/01-intro.pdf) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #pragma once | ||
|
|
||
| #include "eremin_v_rectangle_method/common/include/common.hpp" | ||
| #include "task/include/task.hpp" | ||
|
|
||
| namespace eremin_v_rectangle_method { | ||
|
|
||
| class EreminVRectangleMethodSEQ : public BaseTask { | ||
| public: | ||
| static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { | ||
| return ppc::task::TypeOfTask::kSEQ; | ||
| } | ||
| explicit EreminVRectangleMethodSEQ(const InType &in); | ||
|
|
||
| private: | ||
| bool ValidationImpl() override; | ||
| bool PreProcessingImpl() override; | ||
| bool RunImpl() override; | ||
| bool PostProcessingImpl() override; | ||
| }; | ||
|
|
||
| } // namespace eremin_v_rectangle_method |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| #include "eremin_v_rectangle_method/seq/include/ops_seq.hpp" | ||
|
|
||
| #include <cmath> | ||
| #include <tuple> | ||
|
|
||
| #include "eremin_v_rectangle_method/common/include/common.hpp" | ||
|
|
||
| namespace eremin_v_rectangle_method { | ||
|
|
||
| EreminVRectangleMethodSEQ::EreminVRectangleMethodSEQ(const InType &in) { | ||
| SetTypeOfTask(GetStaticTypeOfTask()); | ||
| GetInput() = in; | ||
| GetOutput() = 0; | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodSEQ::ValidationImpl() { | ||
| auto &input = GetInput(); | ||
| return (std::get<0>(input) < std::get<1>(input)) && (std::get<2>(input) > 0) && (std::get<2>(input) <= 100000000) && | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why these checks are necessary?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Проверка, что количество разбиений положительное, и не какое-то огромное число. Границу можно поставить и выше, но для func тестов это уже слишком много, так как они ограничены 1 секундой, а для perf больше не надо, так как и так время выполнение 4-5 секунд |
||
| (std::get<0>(input) >= -1e9) && (std::get<0>(input) <= 1e9) && (std::get<1>(input) >= -1e9) && | ||
| (std::get<1>(input) <= 1e9) && (GetOutput() == 0); | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodSEQ::PreProcessingImpl() { | ||
| return true; | ||
|
allnes marked this conversation as resolved.
|
||
| } | ||
|
|
||
| bool EreminVRectangleMethodSEQ::RunImpl() { | ||
| auto &input = GetInput(); | ||
| const double lower_bound = std::get<0>(input); | ||
| const double upper_bound = std::get<1>(input); | ||
| const int steps = std::get<2>(input); | ||
| const auto &in_function = std::get<3>(input); | ||
| double step_size = (upper_bound - lower_bound) / steps; | ||
| double result = 0.0; | ||
|
|
||
| for (int i = 0; i < steps; i++) { | ||
| result += in_function(lower_bound + ((static_cast<double>(i) + 0.5) * step_size)); | ||
| } | ||
|
|
||
| result *= step_size; | ||
| GetOutput() = result; | ||
| return true; | ||
| } | ||
|
|
||
| bool EreminVRectangleMethodSEQ::PostProcessingImpl() { | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace eremin_v_rectangle_method | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "tasks_type": "processes", | ||
| "tasks": { | ||
| "mpi": "enabled", | ||
| "seq": "enabled" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| InheritParentConfig: true | ||
|
|
||
| Checks: > | ||
| -modernize-loop-convert, | ||
| -cppcoreguidelines-avoid-goto, | ||
| -cppcoreguidelines-avoid-non-const-global-variables, | ||
| -misc-use-anonymous-namespace, | ||
| -modernize-use-std-print, | ||
| -modernize-type-traits | ||
|
|
||
| CheckOptions: | ||
| - key: readability-function-cognitive-complexity.Threshold | ||
| value: 50 # Relaxed for tests |
Uh oh!
There was an error while loading. Please reload this page.