Skip to content
Closed
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
17 changes: 17 additions & 0 deletions tasks/kutergin_a_closest_pair/common/include/common.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <string>
#include <tuple>
#include <vector>

#include "task/include/task.hpp"

namespace kutergin_a_closest_pair {

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

} // namespace kutergin_a_closest_pair
9 changes: 9 additions & 0 deletions tasks/kutergin_a_closest_pair/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ФИ1",
"task_number": "7"
}
}
22 changes: 22 additions & 0 deletions tasks/kutergin_a_closest_pair/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

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

namespace kutergin_a_closest_pair {

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

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

} // namespace kutergin_a_closest_pair
162 changes: 162 additions & 0 deletions tasks/kutergin_a_closest_pair/mpi/src/ops_mpi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#include "kutergin_a_closest_pair/mpi/include/ops_mpi.hpp"

#include <mpi.h>

#include <algorithm>
#include <limits>
#include <random>
#include <vector>

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

namespace kutergin_a_closest_pair {

KuterginAClosestPairMPI::KuterginAClosestPairMPI(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = -1;
}

bool KuterginAClosestPairMPI::ValidationImpl() {
return true;
}

bool KuterginAClosestPairMPI::PreProcessingImpl() {
return true;
}

// Вспомогательные функции (не методы класса)
namespace {

std::vector<int> DistributeData(int rank, int size, int n, const std::vector<int> &v) {
int local_size = n / size;
int remainder = n % size;

int start = rank * local_size + std::min(rank, remainder);
int end = start + local_size + (rank < remainder ? 1 : 0);

if (rank == size - 1) {
end = n;
}

std::vector<int> local_data(end - start);
if (rank == 0) {
std::copy(v.begin() + start, v.begin() + end, local_data.begin());

for (int i = 1; i < size; ++i) {
int other_start = i * local_size + std::min(i, remainder);
int other_end = other_start + local_size + (i < remainder ? 1 : 0);
if (i == size - 1) {
other_end = n;
}

MPI_Send(v.data() + other_start, other_end - other_start, MPI_INT, i, 0, MPI_COMM_WORLD);
}
} else {
MPI_Recv(local_data.data(), static_cast<int>(local_data.size()), MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}

return local_data;
}

int FindLocalMin(const std::vector<int> &local_data, int start_idx, int &found_idx) {
int local_min = std::numeric_limits<int>::max();
found_idx = -1;

for (int i = 0; i < static_cast<int>(local_data.size()) - 1; ++i) {
int diff = std::abs(local_data[i + 1] - local_data[i]);
if (diff < local_min) {
local_min = diff;
found_idx = start_idx + i;
}
}

return local_min;
}

int CalculateStartIndex(int rank, int size, int n) {
int local_size = n / size;
int remainder = n % size;
return rank * local_size + std::min(rank, remainder);
}

int CalculateEndIndex(int rank, int size, int n) {
int local_size = n / size;
int remainder = n % size;
int end = CalculateStartIndex(rank, size, n) + local_size + (rank < remainder ? 1 : 0);
if (rank == size - 1) {
end = n;
}
return end;
}

int CheckBoundary(int rank, int size, int end, int n, const std::vector<int> &v, const std::vector<int> &local_data,
int current_min, int &current_idx) {
if (rank < size - 1 && end < n) {
int boundary_diff = std::abs(v[end] - local_data.back());
if (boundary_diff < current_min) {
current_min = boundary_diff;
current_idx = end - 1;
}
}
return current_min;
}

} // namespace

bool KuterginAClosestPairMPI::RunImpl() {
int rank = 0;
int size = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

const auto &v = GetInput();
int n = static_cast<int>(v.size());

MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

if (n < 2) {
GetOutput() = -1;
return true;
}

// Распределение данных
auto local_data = DistributeData(rank, size, n, v);
if (local_data.empty()) {
GetOutput() = -1;
return true;
}

// Локальный поиск
int start_idx = CalculateStartIndex(rank, size, n);
int local_idx = -1;
int local_min = FindLocalMin(local_data, start_idx, local_idx);

// Проверка границ
int end = CalculateEndIndex(rank, size, n);
local_min = CheckBoundary(rank, size, end, n, v, local_data, local_min, local_idx);

// Глобальная редукция
struct MinIndex {
int val = 0;
int idx = -1;
};

MinIndex local_result;
local_result.val = local_min;
local_result.idx = local_idx;

MinIndex global_result;

MPI_Allreduce(&local_result, &global_result, 1, MPI_2INT, MPI_MINLOC, MPI_COMM_WORLD);

GetOutput() = global_result.idx;
return true;
}

bool KuterginAClosestPairMPI::PostProcessingImpl() {
return true;
}

} // namespace kutergin_a_closest_pair
51 changes: 51 additions & 0 deletions tasks/kutergin_a_closest_pair/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Нахождение наиболее близких соседних элементов вектора

- Student: Кутергин Антон Андреевич, group 3823Б1ФИ1
- Technology: SEQ | MPI
- Variant: 7

## 1 Введение

Нужно было реализовать параллельный и последовательный алгоритм для поиска наиболее близких соседних элементов вектора

## 2 Постановка задачи

На вход подается вектор v, нужно найти такой индекс i, что велечина |v[i] - v[i+1]| минимальна среди всех пар элемента

## 3. Baseline Algorithm (Sequential)

Последовательно просматривает пары v[i], v[i+1], сравнивает разности и возвращает индекс максимума

## 4. Parallelization Scheme

Вектор разбивается на MPI процессы, корректно обрабатывая границы между процессами, чтобы последнему числу этого процесса и первому следующего процесса также образуют пару. Каждый ищет свой локальный минимум среди всех своих соседних пар. Возвращаем глобальный индекс пары с помощью MPI_Reduce

## 6. Experimental Setup

-HARDWARE/OS: CPU - Intel Core i5-8300H, cores/threads - 4/8, RAM - 12gb, OS - Ubuntu 24.04 (DevContainer / WSL 2)
TOOLCHAIN: g++ 13.3.0, build type - Release


## 7. Results and Discussion

### 7.1 Correctness

Функциональные тесты: вектор может принимать как положительные, так и отрицательные значения или содержать и положительные и отрицательные значения, так же обрабатывается случай, когда вектор пуст или у него одно значение и пару ему не найти.
MPI версия запускалась на 4 ядрах - эталонное ускорение в 4 раза.
Тест на производительность: генерация n чисел и запуск алгоритмов, чтобы узнать время выполнения.

### 7.2 Performance

| Размер данных | MPI версия (сек) | SEQ версия (сек) | Ускорение |
|---------------|------------------------|-------------------------|-----------|
| 10,000,000 | 0.0269160954 | 0.0310927963 | 1.22x |
| 100,000,000 | 0.411453807 | 0.5857917309 | 1.39x |


## 8. Conclusions

Алгоритм нахождения наиболее близких соседних элементов вектора работает как последовательно, так и параллельно. Чем больше данных, тем быстрее работает MPI версия алгоритма, но не сильно, т.к. в задаче мы просто проходим по вектору и находим разницу и накладные расходы MPI не особо много выигрывают.

## 9. References
1. Лекции по параллельному программированию
2. Практические занятия по параллельному программированию
23 changes: 23 additions & 0 deletions tasks/kutergin_a_closest_pair/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

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

namespace kutergin_a_closest_pair {

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

private:
std::vector<int> data_;
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace kutergin_a_closest_pair
55 changes: 55 additions & 0 deletions tasks/kutergin_a_closest_pair/seq/src/ops_seq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "kutergin_a_closest_pair/seq/include/ops_seq.hpp"

#include <cmath>
#include <limits>
#include <vector>

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

namespace kutergin_a_closest_pair {

KuterginAClosestPairSEQ::KuterginAClosestPairSEQ(const InType &in) : data_() {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = -1;
data_ = in;
}

bool KuterginAClosestPairSEQ::ValidationImpl() {
return true;
}

bool KuterginAClosestPairSEQ::PreProcessingImpl() {
return true;
}

bool KuterginAClosestPairSEQ::RunImpl() {
const auto &v = GetInput();
int n = static_cast<int>(v.size());

if (n < 2) {
GetOutput() = -1;
return true;
}

int min_diff = std::numeric_limits<int>::max();
int best_idx = -1;

for (int i = 0; i < n - 1; ++i) {
int diff = std::abs(v[i + 1] - v[i]);
if (diff < min_diff) {
min_diff = diff;
best_idx = i;
}
}

GetOutput() = best_idx;
return true;
}

bool KuterginAClosestPairSEQ::PostProcessingImpl() {
return true;
}

} // namespace kutergin_a_closest_pair
7 changes: 7 additions & 0 deletions tasks/kutergin_a_closest_pair/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/kutergin_a_closest_pair/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