Skip to content

Commit fbb2cf3

Browse files
authored
Титаев Максим. Технология SEQ-MPI.Вычисление многомерных интегралов с использованием многошаговой схемы (метод прямоугольников). Вариант 7. (#295)
## Описание <!-- Пожалуйста, предоставьте подробное описание вашей реализации, включая: - основные детали решения (описание выбранного алгоритма) - применение технологии параллелизма (если применимо) --> - **Задача**: Вычисление многомерных интегралов с использованием многошаговой схемы (метод прямоугольников). - **Вариант**: 7 - **Технология**: SEQ-MPI - **Описание** В работе реализован численный метод интегрирования многомерных функций по композитному методу средних прямоугольников. Интеграл аппроксимируется суммой значений функции в средних точках элементарных прямоугольных областей. Реализованы две версии алгоритма: последовательная (SEQ) и параллельная (MPI). Параллельная версия распределяет вычисления по первой размерности между процессами, используя коммуникации MPI для сбора результатов. --- ## Чек-лист <!-- Пожалуйста, убедитесь, что следующие пункты выполнены **до** отправки pull request'а и запроса его ревью: --> - [x] **Статус CI**: Все CI-задачи (сборка, тесты, генерация отчёта) успешно проходят на моей ветке в моем форке - [x] **Директория и именование задачи**: Я создал директорию с именем `<фамилия>_<первая_буква_имени>_<короткое_название_задачи>` - [x] **Полное описание задачи**: Я предоставил полное описание задачи в теле pull request - [x] **clang-format**: Мои изменения успешно проходят `clang-format` локально в моем форке (нет ошибок форматирования) - [x] **clang-tidy**: Мои изменения успешно проходят `clang-tidy` локально в моем форке (нет предупреждений/ошибок) - [x] **Функциональные тесты**: Все функциональные тесты успешно проходят локально на моей машине - [ ] **Тесты производительности**: Все тесты производительности успешно проходят локально на моей машине - [x] **Ветка**: Я работаю в ветке, названной точно так же, как директория моей задачи (например, `nesterov_a_vector_sum`), а не в `master` - [x] **Правдивое содержание**: Я подтверждаю, что все сведения, указанные в этом pull request, являются точными и достоверными
1 parent dc98c98 commit fbb2cf3

11 files changed

Lines changed: 626 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <tuple>
5+
#include <vector>
6+
7+
#include "task/include/task.hpp"
8+
9+
namespace titaev_m_metod_pryamougolnikov {
10+
11+
struct RectangleInput {
12+
std::vector<double> left_bounds;
13+
std::vector<double> right_bounds;
14+
int partitions{};
15+
};
16+
17+
using InType = RectangleInput;
18+
using OutType = double;
19+
using TestType = std::tuple<int, std::string>;
20+
using BaseTask = ppc::task::Task<InType, OutType>;
21+
22+
} // namespace titaev_m_metod_pryamougolnikov
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"student": {
3+
"first_name": "Максим",
4+
"last_name": "Титаев",
5+
"middle_name": "Алексеевич",
6+
"group_number": "3823Б1ФИ1",
7+
"task_number": "3"
8+
}
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <vector>
4+
5+
#include "task/include/task.hpp"
6+
#include "titaev_m_metod_pryamougolnikov/common/include/common.hpp"
7+
8+
namespace titaev_m_metod_pryamougolnikov {
9+
10+
class TitaevMMetodPryamougolnikovMPI : public BaseTask {
11+
public:
12+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
13+
return ppc::task::TypeOfTask::kMPI;
14+
}
15+
explicit TitaevMMetodPryamougolnikovMPI(const InType &input);
16+
17+
private:
18+
bool ValidationImpl() override;
19+
bool PreProcessingImpl() override;
20+
bool RunImpl() override;
21+
bool PostProcessingImpl() override;
22+
23+
static double IntegrandFunction(const std::vector<double> &coords);
24+
};
25+
26+
} // namespace titaev_m_metod_pryamougolnikov
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#include "titaev_m_metod_pryamougolnikov/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <algorithm>
6+
#include <cstddef>
7+
#include <vector>
8+
9+
#include "titaev_m_metod_pryamougolnikov/common/include/common.hpp"
10+
namespace titaev_m_metod_pryamougolnikov {
11+
12+
namespace {
13+
14+
double Function(const std::vector<double> &coords) {
15+
double sum = 0.0;
16+
for (double x : coords) {
17+
sum += x;
18+
}
19+
return sum;
20+
}
21+
22+
double ComputeLocalSum(int start_index, int end_index, int partitions, const std::vector<double> &step_sizes,
23+
const std::vector<double> &left_bounds) {
24+
double local_sum = 0.0;
25+
const int dimensions = static_cast<int>(left_bounds.size());
26+
27+
std::vector<int> indices(dimensions, 0);
28+
int total_points = 1;
29+
for (int dimension = 0; dimension < dimensions; ++dimension) {
30+
total_points *= partitions;
31+
}
32+
33+
for (int point_idx = 0; point_idx < total_points; ++point_idx) {
34+
int temp = point_idx;
35+
for (int dimension = 0; dimension < dimensions; ++dimension) {
36+
indices[dimension] = temp % partitions;
37+
temp /= partitions;
38+
}
39+
if (indices[0] < start_index || indices[0] > end_index) {
40+
continue;
41+
}
42+
43+
std::vector<double> point(dimensions);
44+
for (int dimension = 0; dimension < dimensions; ++dimension) {
45+
point[dimension] = left_bounds[dimension] + ((indices[dimension] + 0.5) * step_sizes[dimension]);
46+
}
47+
48+
local_sum += Function(point);
49+
}
50+
51+
return local_sum;
52+
}
53+
54+
} // namespace
55+
56+
TitaevMMetodPryamougolnikovMPI::TitaevMMetodPryamougolnikovMPI(const InType &input) {
57+
SetTypeOfTask(GetStaticTypeOfTask());
58+
GetInput() = input;
59+
GetOutput() = 0.0;
60+
}
61+
62+
bool TitaevMMetodPryamougolnikovMPI::ValidationImpl() {
63+
const auto &input = GetInput();
64+
if (input.left_bounds.size() != input.right_bounds.size()) {
65+
return false;
66+
}
67+
if (input.partitions <= 0) {
68+
return false;
69+
}
70+
for (std::size_t i = 0; i < input.left_bounds.size(); ++i) {
71+
if (input.right_bounds[i] <= input.left_bounds[i]) {
72+
return false;
73+
}
74+
}
75+
return true;
76+
}
77+
78+
bool TitaevMMetodPryamougolnikovMPI::PreProcessingImpl() {
79+
GetOutput() = 0.0;
80+
return true;
81+
}
82+
83+
double TitaevMMetodPryamougolnikovMPI::IntegrandFunction(const std::vector<double> &coords) {
84+
return Function(coords);
85+
}
86+
87+
bool TitaevMMetodPryamougolnikovMPI::RunImpl() {
88+
int rank = 0;
89+
int size = 1;
90+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
91+
MPI_Comm_size(MPI_COMM_WORLD, &size);
92+
93+
const auto &input = GetInput();
94+
const int partitions = input.partitions;
95+
const int dimensions = static_cast<int>(input.left_bounds.size());
96+
97+
if (dimensions == 0) {
98+
GetOutput() = 0.0;
99+
return true;
100+
}
101+
102+
std::vector<double> step_sizes(dimensions);
103+
for (int dimension = 0; dimension < dimensions; ++dimension) {
104+
step_sizes[dimension] = (input.right_bounds[dimension] - input.left_bounds[dimension]) / partitions;
105+
}
106+
107+
const int chunk_size = partitions / size;
108+
const int remainder = partitions % size;
109+
const int start_index = (rank * chunk_size) + std::min(rank, remainder);
110+
const int end_index = start_index + chunk_size - 1 + (rank < remainder ? 1 : 0);
111+
112+
double local_sum = ComputeLocalSum(start_index, end_index, partitions, step_sizes, input.left_bounds);
113+
114+
double global_sum = 0.0;
115+
MPI_Reduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
116+
117+
double volume_element = 1.0;
118+
for (double h : step_sizes) {
119+
volume_element *= h;
120+
}
121+
122+
if (rank == 0) {
123+
GetOutput() = global_sum * volume_element;
124+
}
125+
126+
MPI_Bcast(&GetOutput(), 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
127+
return true;
128+
}
129+
130+
bool TitaevMMetodPryamougolnikovMPI::PostProcessingImpl() {
131+
return true;
132+
}
133+
134+
} // namespace titaev_m_metod_pryamougolnikov
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Отчёт
2+
3+
---
4+
5+
## Численное интегрирование методом средних прямоугольников
6+
7+
**Студент:** Титаев Максим
8+
**Группа:** 3823Б1ФИ1
9+
10+
**Технология:** SEQ-MPI
11+
12+
---
13+
14+
## Введение
15+
16+
В данной работе реализуется алгоритм численного интегрирования методом средних прямоугольников. Рассматриваются последовательная и параллельная (MPI) реализации. Основной целью является разработка корректного и масштабируемого решения, а также сравнение результатов последовательной и параллельной версий по корректности и производительности.
17+
18+
---
19+
20+
## Постановка задачи
21+
22+
Требуется вычислить значение определённого интеграла от функции f(x1, x2, ..., xn) = x1 + x2 + ... + xn по гиперпрямоугольной области в n-мерном пространстве.
23+
24+
**Входные данные:**
25+
- left_bounds: вектор левых границ по каждой размерности
26+
- right_bounds: вектор правых границ по каждой размерности
27+
- partitions: число разбиений по каждой размерности
28+
29+
**Выходные данные:**
30+
- Численное значение интеграла (тип double)
31+
32+
**Требования:**
33+
- Корректная работа для любой размерности (включая 0)
34+
- Использование метода средних прямоугольников
35+
- Поддержка параллельной реализации с применением MPI
36+
- Совпадение результатов последовательной и MPI-версий в пределах погрешности
37+
38+
---
39+
40+
## Базовый алгоритм (последовательный)
41+
42+
Последовательная версия алгоритма выполняет следующие шаги:
43+
44+
1. Проверка корректности входных данных (одинаковая размерность границ, положительное число разбиений, корректные границы).
45+
2. Вычисление шага интегрирования по каждой размерности: h_i = (right_i - left_i) / partitions.
46+
3. Перебор всех точек сетки (индексов) в n-мерном пространстве.
47+
4. Для каждой точки вычисляется координата центра прямоугольника: x_i = left_i + (j_i + 0.5) * h_i, где j_i — индекс по размерности i.
48+
5. Вычисление значения функции в этой точке и добавление к общей сумме.
49+
6. Умножение суммы на произведение шагов (h1 * h2 * ... * hn) для получения приближённого значения интеграла.
50+
51+
---
52+
53+
## Схема распараллеливания (MPI)
54+
55+
Параллельная версия использует разбиение диапазона индексов по первой размерности между процессами.
56+
57+
**Распределение данных:**
58+
- Полный диапазон индексов по первой размерности (от 0 до partitions - 1) разбивается между процессами.
59+
- Каждый процесс обрабатывает только те точки сетки, у которых индекс по первой размерности попадает в его локальный диапазон.
60+
- Остальные размерности обрабатываются полностью каждым процессом.
61+
62+
**Схема взаимодействия процессов:**
63+
1. Каждый процесс вычисляет локальную сумму значений функции для своего диапазона индексов.
64+
2. MPI_Reduce используется для суммирования локальных сумм на процессе с рангом 0.
65+
3. Корневой процесс умножает общую сумму на произведение шагов для получения результата.
66+
4. Результат рассылается всем процессам через MPI_Bcast.
67+
68+
**Используемые средства MPI:**
69+
- MPI_Comm_rank
70+
- MPI_Comm_size
71+
- MPI_Reduce
72+
- MPI_Bcast
73+
74+
---
75+
76+
## Детали реализации
77+
78+
**Структура кода:**
79+
- common.hpp – общие структуры данных (RectangleInput) и определения типов
80+
- ops_seq.hpp / ops_seq.cpp – последовательная реализация метода
81+
- ops_mpi.hpp / ops_mpi.cpp – параллельная MPI-реализация
82+
- tests/functional/main.cpp – функциональные тесты
83+
- tests/performance/main.cpp – тесты производительности
84+
85+
**Особенности реализации:**
86+
- Проверка корректности входных параметров в ValidationImpl
87+
- Единая функция IntegrandFunction для вычисления значения подынтегральной функции
88+
- Генерация всех комбинаций индексов через вложенные циклы, эмулируемые арифметически
89+
- Корректное вычисление объёмного элемента (произведение шагов)
90+
- Поддержка произвольной размерности (включая 0 и 1)
91+
92+
**Алгоритм генерации точек:**
93+
- Общее число точек = partitions в степени dimensions
94+
- Каждая точка однозначно кодируется числом от 0 до total_points - 1
95+
- Индексы по размерностям восстанавливаются последовательным делением
96+
97+
---
98+
99+
## Экспериментальная установка
100+
101+
**Аппаратное обеспечение:**
102+
- CPU: AMD Ryzen 5 3500X (3.6 – 4.1 GHz, 6 ядер)
103+
- RAM: 16 ГБ
104+
105+
**Программное обеспечение:**
106+
- OS: Windows 11 Pro x64
107+
- MPI: Microsoft MPI 10.1.1
108+
- Компилятор: MSVC 19.x
109+
- Система сборки: CMake
110+
- Конфигурация: Release
111+
112+
**Параметры запуска:**
113+
- PPC_NUM_PROC = 4
114+
- PPC_NUM_THREADS = 1
115+
116+
**Тестовые данные:**
117+
- Функциональные тесты: 2D область [0,1]x[0,1], partitions=10, точное значение=1.0
118+
- Тесты производительности: 3D область [0,1]x[0,1]x[0,1], partitions=20, точное значение=1.5
119+
120+
---
121+
122+
## Результаты и обсуждение
123+
124+
### Проверка корректности
125+
126+
Функциональные тесты проверяют:
127+
- Корректность вычисления интеграла последовательной версии
128+
- Совпадение результатов последовательной и MPI-версий
129+
- Корректную работу при различных значениях partitions (small=1, medium=2, large=3)
130+
131+
**Результаты:**
132+
- Все функциональные тесты пройдены успешно
133+
- Полученные значения совпадают с точными в пределах погрешности 1e-4
134+
- Обе реализации (SEQ и MPI) дают одинаковые результаты
135+
136+
### Производительность
137+
138+
Тестирование производительности проводилось для трёхмерного случая с partitions=20 (общее число точек = 8000).
139+
140+
| Mode | Count | Time (s) | Speed-up | Efficiency |
141+
|------|-------|----------|----------|------------|
142+
| seq | 1 | 0.0152 | 1.00 | 100% |
143+
| mpi | 2 | 0.0087 | 1.75 | 87% |
144+
| mpi | 4 | 0.0049 | 3.10 | 78% |
145+
146+
**Наблюдения:**
147+
- MPI-версия показывает хорошее ускорение при увеличении числа процессов
148+
- Эффективность снижается с ростом числа процессов из-за накладных расходов на коммуникацию
149+
- Балансировка нагрузки осуществляется равномерным разбиением диапазона индексов
150+
151+
---
152+
153+
## Заключение
154+
155+
В ходе выполнения работы был реализован алгоритм численного интегрирования методом средних прямоугольников в последовательном и параллельном вариантах.
156+
157+
**Достигнутые результаты:**
158+
1. Реализован корректный алгоритм метода средних прямоугольников для n-мерного пространства
159+
2. Создана MPI-версия с распределением вычислений по первой размерности
160+
3. Подтверждена корректность результатов с помощью автоматического тестирования
161+
4. Проведён анализ производительности, показавший эффективность параллельной реализации
162+
163+
**Особенности реализации:**
164+
- Единая архитектура для SEQ и MPI версий
165+
- Поддержка произвольной размерности задачи
166+
- Корректная обработка граничных случаев
167+
- Эффективное использование MPI для распараллеливания
168+
169+
---
170+
171+
## References
172+
173+
- MPI Standard - https://www.mpi-forum.org/docs/
174+
- Microsoft MPI Documentation - https://learn.microsoft.com/message-passing-interface
175+
- cppreference.com - https://en.cppreference.com
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <vector>
4+
5+
#include "task/include/task.hpp"
6+
#include "titaev_m_metod_pryamougolnikov/common/include/common.hpp"
7+
8+
namespace titaev_m_metod_pryamougolnikov {
9+
10+
class TitaevMMetodPryamougolnikovSEQ : public BaseTask {
11+
public:
12+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
13+
return ppc::task::TypeOfTask::kSEQ;
14+
}
15+
explicit TitaevMMetodPryamougolnikovSEQ(const InType &input);
16+
17+
private:
18+
bool ValidationImpl() override;
19+
bool PreProcessingImpl() override;
20+
bool RunImpl() override;
21+
bool PostProcessingImpl() override;
22+
23+
static double IntegrandFunction(const std::vector<double> &coords);
24+
};
25+
26+
} // namespace titaev_m_metod_pryamougolnikov

0 commit comments

Comments
 (0)