Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions tasks/eremin_v_rectangle_method/common/include/common.hpp
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)>>;
Comment thread
allnes marked this conversation as resolved.
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
Binary file added tasks/eremin_v_rectangle_method/data/int.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions tasks/eremin_v_rectangle_method/info.json
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"
}
}
22 changes: 22 additions & 0 deletions tasks/eremin_v_rectangle_method/mpi/include/ops_mpi.hpp
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
70 changes: 70 additions & 0 deletions tasks/eremin_v_rectangle_method/mpi/src/ops_mpi.cpp
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 {

Comment thread
allnes marked this conversation as resolved.
EreminVRectangleMethodMPI::EreminVRectangleMethodMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0;
Comment thread
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() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data is not being scattered from rank 0 to other ranks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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;
Comment thread
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
157 changes: 157 additions & 0 deletions tasks/eremin_v_rectangle_method/report.md
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. Постановка задачи

**Задача**: Требуется вычислить значение определенного интеграла методом прямоугольников

![Интеграл](data/int.png)

где:
- `f(x)` — интегрируемая функция
- `a`, `b` — нижний и верхний пределы интегрирования соответственно

**Описание метода решения:** Из трех методов (левых, правых и средних прямоугольников) был выбран *метод средних прямоугольников*.

Область интегрирования разбивается на `n` равных отрезков. Сложная площадь под кривой разбивается на множество простых прямоугольников - на каждом отрезке строится прямоугольник высотой, равной значению функции в середине отрезка. Общая площадь вычисляется как сумма площадей всех этих прямоугольников.

![Метод средних прямоугольников](data/square.png)

**Входные данные:**
- `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)

22 changes: 22 additions & 0 deletions tasks/eremin_v_rectangle_method/seq/include/ops_seq.hpp
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
49 changes: 49 additions & 0 deletions tasks/eremin_v_rectangle_method/seq/src/ops_seq.cpp
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) &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why these checks are necessary?

(std::get<2>(input) > 0) && (std::get<2>(input) <= 100000000)

Copy link
Copy Markdown
Contributor Author

@ErVasyaVC ErVasyaVC Nov 22, 2025

Choose a reason for hiding this comment

The 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;
Comment thread
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
7 changes: 7 additions & 0 deletions tasks/eremin_v_rectangle_method/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks_type": "processes",
"tasks": {
"mpi": "enabled",
"seq": "enabled"
}
}
13 changes: 13 additions & 0 deletions tasks/eremin_v_rectangle_method/tests/.clang-tidy
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
Loading
Loading