Skip to content

Commit 7553809

Browse files
4elodoy-Molovek4elodoy-Molovek
andauthored
Дорофеев Иван. Технология SEQ-MPI. Интегрирование – метод Монте-Карло. Вариант 21. (#65)
## Описание **SEQ:** Последовательный алгоритм основан на статистической оценке определённого интеграла по набору случайных точек. Сначала определяется область интегрирования и количество выборок. Затем генерируются случайные точки, равномерно распределённые внутри данной области, на каждой точке вычисляется значение функции, после чего среднее значение умножается на объём области, что и даёт приближённый результат интегрирования. **MPI:** В параллельной версии общее количество выборок делится между всеми процессами. Каждый процесс генерирует свой набор случайных точек и вычисляет промежуточную сумму значений функции. Для этого общее число выборок разбивается на число блоков, равное числу процессов. Размеры блоков одинаковы, за исключением последнего процесса, которому выделяются остаточные выборки. После выполнения локальных вычислений результаты всех процессов суммируются с помощью операции MPI_Reduce, и итоговый результат собирается на нулевом процессе. Отчёт содержит описание системы, на которой проводились эксперименты, результаты и вывод. --- ## Чек-лист <!-- Пожалуйста, убедитесь, что следующие пункты выполнены **до** отправки pull request'а и запроса его ревью: --> - [x] **Статус CI**: Все CI-задачи (сборка, тесты, генерация отчёта) успешно проходят на моей ветке в моем форке - [x] **Директория и именование задачи**: Я создал директорию с именем `<фамилия>_<первая_буква_имени>_<короткое_название_задачи>` - [x] **Полное описание задачи**: Я предоставил полное описание задачи в теле pull request - [x] **clang-format**: Мои изменения успешно проходят `clang-format` локально в моем форке (нет ошибок форматирования) - [x] **clang-tidy**: Мои изменения успешно проходят `clang-tidy` локально в моем форке (нет предупреждений/ошибок) - [x] **Функциональные тесты**: Все функциональные тесты успешно проходят локально на моей машине - [x] **Тесты производительности**: Все тесты производительности успешно проходят локально на моей машине - [x] **Ветка**: Я работаю в ветке, названной точно так же, как директория моей задачи (например, `nesterov_a_vector_sum`), а не в `master` - [x] **Правдивое содержание**: Я подтверждаю, что все сведения, указанные в этом pull request, являются точными и достоверными <!-- ПРИМЕЧАНИЕ: Ложные сведения в этом чек-листе могут привести к отклонению PR и получению нулевого балла за соответствующую задачу. --> --------- Co-authored-by: 4elodoy-Molovek <ivandor22@outlook.com>
1 parent 0163d08 commit 7553809

11 files changed

Lines changed: 698 additions & 0 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <string>
5+
#include <tuple>
6+
#include <vector>
7+
8+
#include "task/include/task.hpp"
9+
10+
namespace dorofeev_i_monte_carlo_integration_processes {
11+
12+
struct InputData {
13+
std::function<double(const std::vector<double> &)> func; // f(x)
14+
std::vector<double> a; // lower bounds
15+
std::vector<double> b; // upper bounds
16+
int samples = 0; // number of samples
17+
};
18+
19+
using InType = InputData;
20+
using OutType = double;
21+
22+
using TestType = std::tuple<int, std::string>; // for testing purposes
23+
using BaseTask = ppc::task::Task<InType, OutType>;
24+
25+
} // namespace dorofeev_i_monte_carlo_integration_processes
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": "1"
8+
}
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "dorofeev_i_monte_carlo_integration/common/include/common.hpp"
4+
#include "task/include/task.hpp"
5+
6+
namespace dorofeev_i_monte_carlo_integration_processes {
7+
8+
class DorofeevIMonteCarloIntegrationMPI : public BaseTask {
9+
public:
10+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
11+
return ppc::task::TypeOfTask::kMPI;
12+
}
13+
explicit DorofeevIMonteCarloIntegrationMPI(const InType &in);
14+
15+
private:
16+
bool ValidationImpl() override;
17+
bool PreProcessingImpl() override;
18+
bool RunImpl() override;
19+
bool PostProcessingImpl() override;
20+
};
21+
22+
} // namespace dorofeev_i_monte_carlo_integration_processes
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include "dorofeev_i_monte_carlo_integration/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <algorithm>
6+
#include <cstddef>
7+
#include <random>
8+
#include <ranges>
9+
#include <utility>
10+
#include <vector>
11+
12+
#include "dorofeev_i_monte_carlo_integration/common/include/common.hpp"
13+
14+
namespace dorofeev_i_monte_carlo_integration_processes {
15+
16+
DorofeevIMonteCarloIntegrationMPI::DorofeevIMonteCarloIntegrationMPI(const InType &in) {
17+
SetTypeOfTask(GetStaticTypeOfTask());
18+
GetInput() = in;
19+
GetOutput() = 0;
20+
}
21+
22+
bool DorofeevIMonteCarloIntegrationMPI::ValidationImpl() {
23+
int rank = 0;
24+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
25+
26+
bool valid = true;
27+
28+
if (rank == 0) {
29+
const auto &in = GetInput();
30+
valid =
31+
in.func && !in.a.empty() && in.a.size() == in.b.size() &&
32+
std::ranges::all_of(std::views::iota(size_t{0}, in.a.size()), [&](size_t i) { return in.b[i] > in.a[i]; }) &&
33+
in.samples > 0;
34+
}
35+
36+
MPI_Bcast(&valid, 1, MPI_C_BOOL, 0, MPI_COMM_WORLD);
37+
return valid;
38+
}
39+
40+
bool DorofeevIMonteCarloIntegrationMPI::PreProcessingImpl() {
41+
GetOutput() = 0.0;
42+
return true;
43+
}
44+
45+
bool DorofeevIMonteCarloIntegrationMPI::RunImpl() {
46+
int rank = 0;
47+
int size = 0;
48+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
49+
MPI_Comm_size(MPI_COMM_WORLD, &size);
50+
51+
// BROADCAST INPUT
52+
InType in;
53+
54+
if (rank == 0) {
55+
in = GetInput();
56+
}
57+
58+
// BROADCAST DIMS
59+
int dims = static_cast<int>(rank == 0 ? in.a.size() : 0);
60+
MPI_Bcast(&dims, 1, MPI_INT, 0, MPI_COMM_WORLD);
61+
62+
// resize on other ranks
63+
if (rank != 0) {
64+
in.a.resize(dims);
65+
in.b.resize(dims);
66+
}
67+
68+
// BROADCAST BOUNDS
69+
MPI_Bcast(in.a.data(), dims, MPI_DOUBLE, 0, MPI_COMM_WORLD);
70+
MPI_Bcast(in.b.data(), dims, MPI_DOUBLE, 0, MPI_COMM_WORLD);
71+
72+
// BROADCAST SAMPLES
73+
MPI_Bcast(&in.samples, 1, MPI_INT, 0, MPI_COMM_WORLD);
74+
75+
// BROADCAST FUNC ID
76+
int func_id = (rank == 0 ? 1 : 0);
77+
MPI_Bcast(&func_id, 1, MPI_INT, 0, MPI_COMM_WORLD);
78+
79+
// restore the function
80+
switch (func_id) {
81+
case 1:
82+
in.func = [](const std::vector<double> &x) { return x[0] * x[0]; };
83+
break;
84+
default:
85+
in.func = nullptr;
86+
}
87+
88+
// CALCULATIONS
89+
int n_total = in.samples;
90+
int n_local = n_total / size;
91+
if (rank == size - 1) {
92+
n_local += n_total % size;
93+
}
94+
95+
std::vector<std::uniform_real_distribution<double>> dist;
96+
dist.reserve(dims);
97+
for (int dim = 0; std::cmp_less(dim, dims); dim++) {
98+
dist.emplace_back(in.a[dim], in.b[dim]);
99+
}
100+
101+
std::mt19937 gen(rank + 777);
102+
double local_sum = 0.0;
103+
104+
std::vector<double> x;
105+
x.assign(static_cast<size_t>(dims), 0.0);
106+
107+
for (int i = 0; i < n_local; ++i) {
108+
for (int dim = 0; std::cmp_less(dim, dims); dim++) {
109+
x[static_cast<size_t>(dim)] = dist[dim](gen);
110+
}
111+
local_sum += in.func(x);
112+
}
113+
114+
// REDUCE
115+
double global_sum = 0.0;
116+
MPI_Reduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
117+
118+
// RESULT
119+
double result = 0.0;
120+
if (rank == 0) {
121+
double volume = 1.0;
122+
for (int dim = 0; std::cmp_less(dim, dims); dim++) {
123+
volume *= (in.b[dim] - in.a[dim]);
124+
}
125+
result = (global_sum / n_total) * volume;
126+
}
127+
128+
MPI_Bcast(&result, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
129+
130+
GetOutput() = result;
131+
return true;
132+
}
133+
134+
bool DorofeevIMonteCarloIntegrationMPI::PostProcessingImpl() {
135+
return true;
136+
}
137+
138+
} // namespace dorofeev_i_monte_carlo_integration_processes
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Интегрирование – метод Монте-Карло
2+
3+
**Student:** Дорофеев Иван Денисович, group 3823Б1ФИ1
4+
**Technology:** SEQ | MPI
5+
**Variant:** 21
6+
7+
---
8+
9+
## 1. Introduction
10+
11+
Метод Монте-Карло — один из универсальных способов численного интегрирования, позволяющий эффективно оценивать интегралы любой размерности. Однако точность метода и время работы напрямую зависят от количества случайных выборок (samples). Увеличение количества выборок повышает точность, но также увеличивает время выполнения, что делает задачу подходящей для распараллеливания.
12+
13+
Ожидается, что MPI-версия позволит уменьшить время выполнения за счёт распределения выборок между процессами.
14+
15+
---
16+
17+
## 2. Problem statement
18+
19+
Требуется вычислить значение определённого интеграла методом Монте-Карло.
20+
21+
### Входные данные:
22+
23+
- Границы интегрирования:
24+
$a = 0,\quad b = 1$
25+
26+
- Количество выборок: 190000
27+
28+
- Функция:
29+
$f(x) = x^2$
30+
31+
### Выходные данные:
32+
33+
- Одно действительное число — приближённое значение интеграла.
34+
35+
---
36+
37+
## 3. Baseline Algorithm (Sequential)
38+
39+
1. Сгенерировать N равномерных случайных точек из интервала $[a, b]$.
40+
41+
2. Вычислить значение функции в каждой точке.
42+
43+
3. Найти среднее всех вычисленных значений.
44+
45+
4. Умножить среднее на длину интервала $(b-a)$.
46+
47+
Последовательная реализация проста, но требует выполнения всех N вычислений в одном процессе.
48+
49+
---
50+
51+
## 4. Parallelization Scheme (MPI)
52+
53+
Для распараллеливания используется идея независимости выборок:
54+
каждый процесс может самостоятельно выполнить часть выборок и затем отправить свой частичный результат.
55+
56+
### Схема:
57+
58+
1. Корневой процесс рассылает параметры задачи всем участникам.
59+
60+
2. Каждый процесс:
61+
62+
- выполняет часть выборок:
63+
$N_p = \frac{N}{P} $
64+
65+
- рассчитывает среднее значение функции на своём подмножестве.
66+
67+
3. Результаты всех процессов объединяются с помощью **MPI_Reduce**.
68+
69+
4. Корневой процесс вычисляет итоговый интеграл.
70+
71+
Поскольку метод Монте-Карло "естественно" распараллеливается, эффективность MPI в идеальных условиях может быть высокой.
72+
73+
---
74+
75+
## 5. Experimental Setup
76+
77+
- Hardware/OS:
78+
- CPU: 13th Gen Intel i5-13420H (12) @ 4.6GHz, 8 ядер
79+
- RAM: 16GB RAM
80+
- OS: Ubuntu 25.10 x86_64
81+
- Среда выполнения: Docker (Ubuntu trixie/sid (noble) x86_64)
82+
- Toolchain: compiler, version, build type (Release/RelWithDebInfo)
83+
- CMake 3.28.3
84+
- g++ 13.3.0
85+
- OpenMPI
86+
- Сборка: Release
87+
- Data:
88+
- Количество выборок: 190000
89+
- Функция: $f(x)=x^2$
90+
- Тесты производительности запускались для **SEQ**, **MPI: 2**, **MPI: 4** процессов.
91+
92+
---
93+
94+
## 6. Results and Discussion
95+
96+
### 6.1 Correctness
97+
98+
Корректность проверена тестами GoogleTest.
99+
Точное значение интеграла:
100+
101+
$\int_0^1 x^2 dx = \frac{1}{3} \approx 0.333333 $
102+
103+
104+
Все варианты укладываются в допуск ±0.05.
105+
106+
---
107+
108+
## 6.2 Performance
109+
110+
| Mode | Processes | Time (s) | Speedup | Efficiency |
111+
| ------- | --------- | ------------ | -------- | ---------- |
112+
| **seq** | 1 | **0.002277** | **1.00** ||
113+
| **mpi** | 2 | **0.001751** | **1.30** | **65%** |
114+
| **mpi** | 4 | **0.001137** | **2.00** | **50%** |
115+
116+
---
117+
118+
## 7. Discussion
119+
120+
Результаты экспериментов показывают, что MPI-версия действительно работает быстрее последовательной SEQ-реализации при одинаковом количестве выборок.
121+
122+
Для 2 процессов ускорение составляет **1.30×** при эффективности **65%**.
123+
Для 4 процессов ускорение достигает **2.00×**, что соответствует эффективности **50%**.
124+
125+
Эффективность снижается с ростом числа процессов, это ожидаемое поведение для MPI, так как накладные расходы растут, а объём работы, приходящийся на каждый процесс, уменьшается.
126+
127+
Основные источники потерь производительности:
128+
129+
- необходимость синхронизации между процессами,
130+
131+
- выполнение коллективных операций (например, **MPI_Allreduce**),
132+
133+
- накладные расходы MPI, усиливающиеся при запуске внутри Docker-контейнера,
134+
135+
- относительно малый объём вычислений на один процесс при 4-процессном запуске.
136+
137+
Тем не менее ускорение остаётся значительным — даже с учётом накладных расходов распараллеливание уменьшает общее время расчёта примерно вдвое.
138+
139+
---
140+
141+
## 8. Conclusions
142+
143+
Метод Монте-Карло хорошо поддаётся параллелизации, и MPI-версия показывает ощутимое ускорение.
144+
145+
Хотя эффективность не достигает 100% из-за накладных расходов, ускорение в 2 раза при использовании 4 процессов подтверждает преимущества распараллеливания.
146+
147+
MPI-подход особенно полезен при значительно больших объёмах выборок, где накладные расходы становятся менее заметными.
148+
149+
---
150+
151+
## 9. References
152+
153+
1. "Параллельное программирование для кластерных систем" ИИТММ, ННГУ им. Лобачевского
154+
2. [Open MPI Documentation](https://www.open-mpi.org/doc/)
155+
3. [MPI Reference - Message Passing Interface | Microsoft Learn](https://learn.microsoft.com/en-us/message-passing-interface/mpi-reference)
156+
4. [MPI: A Message-Passing Interface Standard](https://www.mpi-forum.org/docs/mpi-5.0/mpi50-report.pdf)
157+
158+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "dorofeev_i_monte_carlo_integration/common/include/common.hpp"
4+
#include "task/include/task.hpp"
5+
6+
namespace dorofeev_i_monte_carlo_integration_processes {
7+
8+
class DorofeevIMonteCarloIntegrationSEQ : public BaseTask {
9+
public:
10+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
11+
return ppc::task::TypeOfTask::kSEQ;
12+
}
13+
explicit DorofeevIMonteCarloIntegrationSEQ(const InType &in);
14+
15+
private:
16+
bool ValidationImpl() override;
17+
bool PreProcessingImpl() override;
18+
bool RunImpl() override;
19+
bool PostProcessingImpl() override;
20+
};
21+
22+
} // namespace dorofeev_i_monte_carlo_integration_processes

0 commit comments

Comments
 (0)