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

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

#include "task/include/task.hpp"

namespace kurpiakov_a_elem_vec_sum {
Comment thread
allnes marked this conversation as resolved.
using InType = std::tuple<int, std::vector<int>>;
Comment thread
allnes marked this conversation as resolved.
using OutType = int64_t;
using TestType = std::tuple<InType, std::string, OutType>;
using BaseTask = ppc::task::Task<InType, OutType>;

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

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

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

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

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

#include <mpi.h>

#include <cmath>
#include <utility>
#include <vector>

#include "kurpiakov_a_elem_vec_sum/common/include/common.hpp"

namespace kurpiakov_a_elem_vec_sum {

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

bool KurpiakovAElemVecSumMPI::ValidationImpl() {
bool res = (GetOutput() == 0) && (std::cmp_equal((std::get<1>(GetInput()).size()), std::get<0>(GetInput())));
return res;
}

bool KurpiakovAElemVecSumMPI::PreProcessingImpl() {
GetOutput() = 0;
return true;
}

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

int total_size = 0;
if (rank == 0) {
total_size = std::get<0>(GetInput());
}

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

if (total_size == 0) {
GetOutput() = 0LL;
return true;
}

std::vector<int> batch(size);
std::vector<int> displs(size);
int batch_size = total_size / size;
int remainder = total_size % size;
for (int i = 0; i < size; ++i) {
batch[i] = batch_size + (i < remainder ? 1 : 0);
displs[i] = (i == 0) ? 0 : displs[i - 1] + batch[i - 1];
}

int local_size = batch[rank];
std::vector<int> local_data(local_size);

int *sendbuf = nullptr;
if (rank == 0) {
sendbuf = const_cast<int *>(std::get<1>(GetInput()).data());
}

MPI_Scatterv(sendbuf, batch.data(), displs.data(), MPI_INT, local_data.data(), local_size, MPI_INT, 0,
MPI_COMM_WORLD);

OutType local_sum = 0LL;
for (int i = 0; i < local_size; ++i) {
local_sum += static_cast<OutType>(local_data[i]);
}

OutType global_sum = 0LL;
MPI_Reduce(&local_sum, &global_sum, 1, MPI_LONG_LONG, MPI_SUM, 0, MPI_COMM_WORLD);

MPI_Bcast(&global_sum, 1, MPI_LONG_LONG, 0, MPI_COMM_WORLD);

GetOutput() = global_sum;

MPI_Barrier(MPI_COMM_WORLD);

return true;
}

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

} // namespace kurpiakov_a_elem_vec_sum
79 changes: 79 additions & 0 deletions tasks/kurpiakov_a_elem_vec_sum/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Сумма элементов вектора

- Студент: Курпяков Алексей Георгиевич, 3823Б1ФИ3
- Технологии: SEQ | MPI
- Вариант: 1

## 1. Введение
В рамках данной работы был реализован алгоритм суммирования элементов вектора: последовательна и параллельная реализация.

## 2. Постановка задачи
Дан вектор v из N элементов. Необходимо найти найти сумму всех его элементов.
Рассмотрим элементы v = {1, 2, 3 ... N}
Решением задачи будет нахождения суммы: sum = v[0] + v[1] + ... + v[N - 1]

## 3. Описание линейного алгоритма

Последовательная версия:
проходимся по всему массиву, собираем префикс сумму по нему.


## 4. Описание схемы параллельного алгоритма
Пусть у нас K процессов, вектор размера N, тогда алгоритм такой:
Процессы делят массив на M частей.
Части выделяются следующим образом:
Сначала считаем минимум, который будет каждый процесс обрабатывать:
batch = N/K
Затем вычисляем остаток от деления:
remainder = M % N
Первые remainder процессов заберут по 1 элементу из массива, остальные отработают только свой пакет.
Затем 0-ой процесс рассылает каждому потоку свой участок памяти и его размер остальным процессам.
После того как каждый процесс отработал свой участок и посчитал сумму внутри своего вектора 0-ой процесс собрает результаты и суммирует их.

## 5. Окружение
- Hardware/OS: CPU - AMD Rizen 7 7840HS, cores - 8, RAM - 16GB, Ubuntu 24 on WSL
- Toolchain: GCC 13, Release
- Environment: PPC_NUM_PROC = 8
- Data: |v| = 10000000, v[i] = i

## 6. Результаты экспериментов и выводы

Функциональные тесты содержат 9 тестов:
- Тест1: пустой вектор
- Тест2: одиночный элемент
- Тест3: положительные числа в векторе
- Тест4: вектор состоящий из нулей
- Тест5: значения с разными знаками
- Тест6: значения граничащие с переполнением типа int
- Тест7: переполнение типа int
- Тест8: только отрицательные числа
- Тест9: вектор четной длины, такой что для любого v[i] существует v[j] = -v[i] (i != j)

Тест на производительность - |v| = {5000000, 7500000 10000000}, v[i] = i

Ниже таблица с результатами Perf тестов.

В таблице представлено: n - размер вектора, время выполнения SEQ и MPI версии для 4-х процессов (в милисекундах):

| Размер данных (n) | SEQ версия (ms)| MPI версия (ms) | Ускорение |
|-------------------|----------------|------------------|----------|
| 5 000 000 | 96 | 114 | 0,84 |
| 7 500 000 | 173,5 | 183 | 0,94 |
| 10 000 000 | 372 | 219 | 1,69 |

*результат был получен при запуске build/bin/ppc_perf_tests*

При замерах была получена данная таблица.
Мнjю не было получено ожидаемое ускорение в 8 раз.
Это нормально, так как чтобы появился рабочий процесс нужно выполнить команду операционной системы для его создания, что занимает много времени в масштабах задачи суммирования элементов вектора.
Операция суммирования отлично векторизуется и выполняется за 1 тик процессора на современных процессорах с архитектурой x86.Так же много времени забирает операция раздачи элементов вектора каждому процессу и рассчет смещений и размеров пераедаваемых пакетов. Поэтому ожидаемо что прирост скорости начнет проявлятся при действительно больших данных.

## 7. Заключение
В результате проделанной работы были реализованы версии MPI, SEQ алгоритма нахождения суммы элементов вектора.
Было так же показано, что MPI версия работает быстрее SEQ при больших данных, а при небольших - наоборот.


## Источники
1. [cppreference.com](https://en.cppreference.com/)
2. [стандарт с++](https://www.open-std.org)
3. [Документация OpenMPI](https://www.open-mpi.org/doc/)
21 changes: 21 additions & 0 deletions tasks/kurpiakov_a_elem_vec_sum/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

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

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

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

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

#include <utility>
#include <vector>

#include "kurpiakov_a_elem_vec_sum/common/include/common.hpp"

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

bool KurpiakovAElemVecSumSEQ::ValidationImpl() {
bool res = (GetOutput() == 0) && (std::cmp_equal((std::get<1>(GetInput()).size()), std::get<0>(GetInput())));
return res;
}

bool KurpiakovAElemVecSumSEQ::PreProcessingImpl() {
GetOutput() = 0;
return true;
}

bool KurpiakovAElemVecSumSEQ::RunImpl() {
std::vector<int> vec = std::get<1>(GetInput());
OutType res = 0LL;
for (const int &it : vec) {
res += static_cast<OutType>(it);
}
GetOutput() = res;
return true;
}

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

} // namespace kurpiakov_a_elem_vec_sum
7 changes: 7 additions & 0 deletions tasks/kurpiakov_a_elem_vec_sum/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks_type": "processes",
"tasks": {
"mpi": "enabled",
"seq": "enabled"
}
}
75 changes: 75 additions & 0 deletions tasks/kurpiakov_a_elem_vec_sum/tests/functional/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <gtest/gtest.h>

#include <array>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <string>
#include <tuple>
#include <vector>

#include "kurpiakov_a_elem_vec_sum/common/include/common.hpp"
#include "kurpiakov_a_elem_vec_sum/mpi/include/ops_mpi.hpp"
#include "kurpiakov_a_elem_vec_sum/seq/include/ops_seq.hpp"
#include "util/include/func_test_util.hpp"
#include "util/include/util.hpp"

namespace kurpiakov_a_elem_vec_sum {
class KurpiakovAElemVecSumFuncTest : public ppc::util::BaseRunFuncTests<InType, OutType, TestType> {
public:
static std::string PrintTestParam(const TestType &test_param) {
return std::get<1>(test_param);
}

protected:
void SetUp() override {
TestType param = std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>(GetParam());
input_data_ = std::get<0>(param);
expected_data_ = std::get<2>(param);
}

bool CheckTestOutputData(OutType &output_data) final {
return (output_data == expected_data_);
}

InType GetTestInputData() final {
return input_data_;
}

private:
InType input_data_{0, {}};
OutType expected_data_{0};
};

namespace {
TEST_P(KurpiakovAElemVecSumFuncTest, ElemVecSum) {
ExecuteTest(GetParam());
}

const std::array<TestType, 10> kTestParam = {
std::make_tuple(std::make_tuple(0, std::vector<int>{}), "test1_empty", 0LL),
std::make_tuple(std::make_tuple(1, std::vector<int>{5}), "test2_single", 5LL),
std::make_tuple(std::make_tuple(3, std::vector<int>{1, 2, 3}), "test3_positive", 6LL),
std::make_tuple(std::make_tuple(3, std::vector<int>{0, 0, 0}), "test4_zeros", 0LL),
std::make_tuple(std::make_tuple(3, std::vector<int>{1, -2, 3}), "test5_mixed", 2LL),
std::make_tuple(std::make_tuple(2, std::vector<int>{2147483646, 1}), "test6_border", 2147483647LL),
std::make_tuple(std::make_tuple(2, std::vector<int>{2147483646, 2147483647}), "test7_overflow", 4294967293LL),
std::make_tuple(std::make_tuple(3, std::vector<int>{-1, -2, -3}), "test8_negative", -6LL),
std::make_tuple(std::make_tuple(4, std::vector<int>{1, -1, 2, -2}), "test9_alternating", 0LL)};

const auto kTestTasksList =
std::tuple_cat(ppc::util::AddFuncTask<kurpiakov_a_elem_vec_sum::KurpiakovAElemVecSumMPI, InType>(
kTestParam, PPC_SETTINGS_kurpiakov_a_elem_vec_sum),
ppc::util::AddFuncTask<kurpiakov_a_elem_vec_sum::KurpiakovAElemVecSumSEQ, InType>(
kTestParam, PPC_SETTINGS_kurpiakov_a_elem_vec_sum));

const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList);

const auto kPerfTestName = KurpiakovAElemVecSumFuncTest::PrintFuncTestName<KurpiakovAElemVecSumFuncTest>;

// NOLINTNEXTLINE
INSTANTIATE_TEST_SUITE_P(KurpiakovAVec, KurpiakovAElemVecSumFuncTest, kGtestValues, kPerfTestName);

} // namespace

} // namespace kurpiakov_a_elem_vec_sum
Loading
Loading