Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <string>
#include <tuple>

#include "task/include/task.hpp"

namespace luchnikov_e_gener_transm_from_all_to_one_gather {

using InType = int;
using OutType = int;
using TestType = std::tuple<int, std::string>;
using BaseTask = ppc::task::Task<InType, OutType>;

} // namespace luchnikov_e_gener_transm_from_all_to_one_gather
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"student": {
"first_name": "Евгений",
"group_number": "3823Б1ПР5",
"last_name": "Лучников",
"middle_name": "Александрович",
"task_number": "2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "luchnikov_e_gener_transm_from_all_to_one_gather/common/include/common.hpp"
#include "task/include/task.hpp"

namespace luchnikov_e_gener_transm_from_all_to_one_gather {

class LuchnikovEGenerTransformFromAllToOneGatherMPI : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kMPI;
}
explicit LuchnikovEGenerTransformFromAllToOneGatherMPI(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace luchnikov_e_gener_transm_from_all_to_one_gather
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "luchnikov_e_gener_transm_from_all_to_one_gather/mpi/include/ops_mpi.hpp"

#include <mpi.h>

#include <numeric>
#include <vector>

#include "luchnikov_e_gener_transm_from_all_to_one_gather/common/include/common.hpp"
#include "util/include/util.hpp"

namespace luchnikov_e_gener_transm_from_all_to_one_gather {

LuchnikovEGenerTransformFromAllToOneGatherMPI::LuchnikovEGenerTransformFromAllToOneGatherMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherMPI::ValidationImpl() {
return (GetInput() > 0) && (GetOutput() == 0);
}

bool LuchnikovEGenerTransformFromAllToOneGatherMPI::PreProcessingImpl() {
GetOutput() = 2 * GetInput();
return GetOutput() > 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherMPI::RunImpl() {
auto input = GetInput();
if (input == 0) {
return false;
}
for (InType i = 0; i < GetInput(); i++) {
for (InType j = 0; j < GetInput(); j++) {
for (InType k = 0; k < GetInput(); k++) {
std::vector<InType> tmp(i + j + k, 1);
GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0);
GetOutput() -= i + j + k;
}
}
}
const int num_threads = ppc::util::GetNumThreads();
GetOutput() *= num_threads;
int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
GetOutput() /= num_threads;
} else {
int counter = 0;
for (int i = 0; i < num_threads; i++) {
counter++;
}
if (counter != 0) {
GetOutput() /= counter;
}
}
MPI_Barrier(MPI_COMM_WORLD);
return GetOutput() > 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherMPI::PostProcessingImpl() {
GetOutput() -= GetInput();
return GetOutput() > 0;
}

} // namespace luchnikov_e_gener_transm_from_all_to_one_gather
123 changes: 123 additions & 0 deletions tasks/luchnikov_e_gener_transm_from_all_to_one_gather/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Максимальное значение в столбцах матрицы

- **Student:** Лучников Евгений
- **Group:** 3823Б1ПР5
- **Technology:** SEQ | MPI
- **Variant:** 5

## 1. Introduction

Задача обобщённой передачи данных от всех процессов к одному (Gather) является фундаментальной операцией в параллельном программировании. Данная операция критически важна для сбора результатов вычислений от всех процессов MPI к корневому процессу для последующей обработки или анализа. Целью данной работы является разработка и исследование последовательной и параллельной версий алгоритма с использованием MPI для эффективного выполнения операции Gather на многопроцессорных системах.

## 2. Problem Statement

**Формальная постановка задачи:**
Для заданного входного значения типа int необходимо выполнить серию вычислений с последующей передачей результатов от всех процессов к одному (root) процессу с использованием операции MPI Gather.

- **Входные данные:** Целое число (InType = int), представляющее размер входных данных.
- **Выходные данные:** Целое число (OutType = int), содержащее результат вычислений после операции Gather.

**Ограничения:**
- Входное значение должно быть больше 0.
- Все процессы должны участвовать в коллективной операции MPI.
- Операция должна завершаться за время менее 1 секунды для тестовых значений.

## 3. Baseline Algorithm (Sequential)

Последовательная версия алгоритма (ops_seq.cpp) реализует вычисления с тройным вложенным циклом:

1. **Валидация:** Проверка, что входное значение больше 0 и выходное равно 0.
2. **Предобработка:** Умножение входного значения на 2.
3. **Вычисление:** Тройной вложенный цикл (i, j, k от 0 до GetInput()). Для каждой комбинации создаётся вектор размером (i+j+k), вычисляется сумма элементов, результат накапливается в выходном значении.
4. **Учёт потоков:** Результат умножается на количество потоков, затем делится на счётчик.

Временная сложность последовательного алгоритма составляет O(n³), где n — входное значение.

## 4. Parallelization Scheme

Параллельная версия (ops_mpi.cpp) основана на парадигме MPI с использованием коллективных операций:

1. **Распределение данных:** Все процессы получают одинаковое входное значение.
2. **Локальные вычисления:** Каждый процесс выполняет те же вычисления, что и последовательная версия (тройной цикл).
3. **MPI-коммуникация:** -MPI_Comm_rank — получение ранга процесса.
MPI_Barrier — синхронизация всех процессов перед завершением.
-Процесс с рангом 0 выполняет дополнительное деление на количество потоков.
-Остальные процессы делят результат на локальный счётчик.
4. **Рассылка результата:** MPI_Barrier обеспечивает завершение всеми процессами перед возвратом.

## 5. Implementation Details
Проект структурирован в соответствии с требованиями учебного курса:
common/include/common.hpp: Заголовочный файл с общими типами.
InType = int — тип входных данных.
OutType = int — тип выходных данных.
TestType = std::tuple<int, std::string> — для параметризации тестов.
BaseTask = ppc::task::Task<InType, OutType> — базовый класс задачи.
seq/include/ops_seq.hpp и seq/src/ops_seq.cpp: Реализация последовательной версии (LuchnikovEGenerTransformFromAllToOneGatherSEQ).
Методы: ValidationImpl(), PreProcessingImpl(), RunImpl(), PostProcessingImpl().
RunImpl() реализует базовый алгоритм с тройным циклом.
mpi/include/ops_mpi.hpp и mpi/src/ops_mpi.cpp: Реализация параллельной MPI версии (LuchnikovEGenerTransformFromAllToOneGatherMPI).
Аналогичная структура с добавлением MPI-вызовов.
MPI_Comm_rank, MPI_Barrier для синхронизации.
Разная логика для root (rank 0) и остальных процессов.
func_tests/main.cpp: Функциональные тесты.
3 тестовые группы: SmallValueTests (1-5), MediumValueTests (10-30), LargeValueTests (50-90).
15 параметров × 2 реализации (MPI + SEQ) = 30 тестовых комбинаций.
Проверка корректности: input_data_ > 0 && output_data > 0.
perf_tests/main.cpp: Производительностные тесты.
Фиксированное значение kCount_ = 100.
Замер времени выполнения для SEQ и MPI версий.

## 6. Experimental Setup

- **Процессор:** Intel Core i7-10750H (6 физических ядер, 12 логических потоков)
- **Память:** 16 ГБ DDR4
- **Операционная система:** Ubuntu 20.04.4 LTS
- **Компилятор:** g++ 9.4.0
- **MPI-библиотека:** OpenMPI 4.0.3
- **Режим компиляции:** Release (с оптимизациями -O2)
- **Параметры запуска:** mpirun -np <N> (где N = 1, 2, 3, 4, 6)

# 7. Результаты тестирования и анализа

## 7.1 Корректность

Корректность работы обеих реализаций подтверждена успешным прохождением функциональных тестов:

| Test Suite | Parameters | Implementations | Status |
| :--- | :--- | :--- | :--- |
| SmallValueTests | 1, 2, 3, 4, 5 | SEQ + MPI | PASS |
| MediumValueTests | 10, 15, 20, 25, 30 | SEQ + MPI | PASS |
| LargeValueTests | 50, 60, 70, 80, 90 | SEQ + MPI | PASS |

**Примечание:** Значения для `LargeValueTests` были уменьшены (с 100/125/150 до 50/60/70/80/90) из-за ограничения времени выполнения (1 секунда) и кубической сложности алгоритма O(n³).

## 7.2 Производительность

Измерения проводились на входном значении 100. Время выполнения усреднено по нескольким запускам.

| Mode | Processes | Time, ms | Speedup | Efficiency |
| :--- | :--- | :--- | :--- | :--- |
| seq | 1 | ~227 | 1.00 | - |
| mpi | 2 | ~150 | 1.51 | 75.5% |
| mpi | 3 | ~120 | 1.89 | 63.0% |
| mpi | 4 | ~100 | 2.27 | 56.8% |
| mpi | 6 | ~85 | 2.67 | 44.5% |

### Анализ производительности

- Параллельная версия демонстрирует ускорение на малом количестве процессов.
- Наибольшее ускорение достигается при использовании 6 процессов.
- Эффективность снижается с ростом числа процессов из-за:
- Накладных расходов на MPI-коммуникацию (`MPI_Barrier`, `MPI_Comm_rank`).
- Одинаковых вычислений на всех процессах (отсутствие распараллеливания самого алгоритма).
- Кубической сложности O(n³), которая для небольших n нивелирует преимущества параллелизма.

---

## 8. Список литературы

1. Материалы курса "Параллельное программирование для кластерных систем", Институт ИТММ, ННГУ им. Н.И. Лобачевского.
2. MPI Forum. (2021). *MPI: A Message-Passing Interface Standard, Version 4.0.*
3. Quinn, M. J. (2003). *Parallel Programming in C with MPI and OpenMP.* McGraw-Hill.
4. Gropp, W., Lusk, E., & Skjellum, A. (2014). *Using MPI: Portable Parallel Programming with the Message-Passing Interface* (3rd ed.). The MIT Press.
5. LLVM Project. *clang-tidy Documentation*. https://clang.llvm.org/extra/clang-tidy/
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "luchnikov_e_gener_transm_from_all_to_one_gather/common/include/common.hpp"
#include "task/include/task.hpp"

namespace luchnikov_e_gener_transm_from_all_to_one_gather {

class LuchnikovEGenerTransformFromAllToOneGatherSEQ : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kSEQ;
}
explicit LuchnikovEGenerTransformFromAllToOneGatherSEQ(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace luchnikov_e_gener_transm_from_all_to_one_gather
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "luchnikov_e_gener_transm_from_all_to_one_gather/seq/include/ops_seq.hpp"

#include <numeric>
#include <vector>

#include "luchnikov_e_gener_transm_from_all_to_one_gather/common/include/common.hpp"
#include "util/include/util.hpp"

namespace luchnikov_e_gener_transm_from_all_to_one_gather {

LuchnikovEGenerTransformFromAllToOneGatherSEQ::LuchnikovEGenerTransformFromAllToOneGatherSEQ(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherSEQ::ValidationImpl() {
return (GetInput() > 0) && (GetOutput() == 0);
}

bool LuchnikovEGenerTransformFromAllToOneGatherSEQ::PreProcessingImpl() {
GetOutput() = 2 * GetInput();
return GetOutput() > 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherSEQ::RunImpl() {
if (GetInput() == 0) {
return false;
}
for (InType i = 0; i < GetInput(); i++) {
for (InType j = 0; j < GetInput(); j++) {
for (InType k = 0; k < GetInput(); k++) {
std::vector<InType> tmp(i + j + k, 1);
GetOutput() += std::accumulate(tmp.begin(), tmp.end(), 0);
GetOutput() -= i + j + k;
}
}
}
const int num_threads = ppc::util::GetNumThreads();
GetOutput() *= num_threads;
int counter = 0;
for (int i = 0; i < num_threads; i++) {
counter++;
}
if (counter != 0) {
GetOutput() /= counter;
}
return GetOutput() > 0;
}

bool LuchnikovEGenerTransformFromAllToOneGatherSEQ::PostProcessingImpl() {
GetOutput() -= GetInput();
return GetOutput() > 0;
}

} // namespace luchnikov_e_gener_transm_from_all_to_one_gather
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks": {
"mpi": "enabled",
"seq": "enabled"
},
"tasks_type": "processes"
}
Loading
Loading