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


#include <vector>

#include "task/include/task.hpp"

namespace nazyrov_a_a_vector_avg {

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


} // namespace nazyrov_a_a_vector_avg
Binary file added tasks/nazyrov_a_a_vector_avg/data/pic.ppm
Binary file not shown.
7 changes: 7 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"student": {
"full_name": "Nazyrov A.A.",
"group_number": "3823B1PR4",
"task_number": "1"
}
}
27 changes: 27 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/mpi/include/ops_mpi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "../../../nazyrov_a_a_vector_avg/common/include/common.hpp"

namespace nazyrov_a_a_vector_avg {

class VectorAvgMPI : public BaseTask {
public:
explicit VectorAvgMPI(const InType &in);
~VectorAvgMPI() override = default;

static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kMPI;
}

protected:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;

private:
int world_rank_{0};
int world_size_{1};
};

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

#include <mpi.h>

#include <numeric>

namespace nazyrov_a_a_vector_avg {

VectorAvgMPI::VectorAvgMPI(const InType &in) : BaseTask() {
GetInput() = in;
MPI_Comm_rank(MPI_COMM_WORLD, &world_rank_);
MPI_Comm_size(MPI_COMM_WORLD, &world_size_);
}

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

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

bool VectorAvgMPI::RunImpl() {
const auto &input = GetInput();
int n = static_cast<int>(input.size());
int local_n = n / world_size_;
int start = world_rank_ * local_n;
int end = (world_rank_ == world_size_ - 1) ? n : start + local_n;

double local_sum = std::accumulate(input.begin() + start, input.begin() + end, 0.0);
int local_count = end - start;

double global_sum = 0.0;
int global_count = 0;

MPI_Reduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&local_count, &global_count, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);

if (world_rank_ == 0) {
GetOutput() = global_sum / static_cast<double>(global_count);
}

return true;
}

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

} // namespace nazyrov_a_a_vector_avg
55 changes: 55 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Вычисление среднего арифметического элементов вектора

- **Студент:** Назыров Анвар Асгатович
- **Группа:** 3823Б1ПР4
- **Технологии:** MPI, SEQ
- **Вариант:** 4

## 1. Постановка задачи
Вычислить среднее арифметическое элементов вектора целых чисел.
- Вход: вектор целых чисел
- Выход: среднее арифметическое (тип double)

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

### Последовательная версия (SEQ)
1. Вычисляется сумма всех элементов вектора
2. Результат делится на количество элементов

### Параллельная версия (MPI)
1. Вектор равномерно распределяется между процессами
2. Каждый процесс вычисляет локальную сумму и количество элементов
3. Выполняется MPI_Reduce для получения глобальной суммы и общего количества
4. Процесс с рангом 0 вычисляет среднее арифметическое

## 3. Реализация
- Входные данные: `std::vector<int>`
- Выходные данные: `double`
- Методы: `ValidationImpl()`, `PreProcessingImpl()`, `RunImpl()`, `PostProcessingImpl()`

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

### Функциональные тесты
| Размер вектора | SEQ (мс) | MPI c 4 процессами (мс) |
|----------------|----------|------------------------|
| 10 | <1 | <1 |
| 100 | <1 | <1 |
| 1000 | <1 | <1 |

### Тесты производительности
| Версия | Время на 1,000,000 элементов |
|--------|------------------------------|
| SEQ | ~1.95 мс |
| MPI (4 процесса) | ~2.10 мс |

### Корректность
- Все тесты пройдены (6 из 6)
- Результаты совпадают с ожидаемыми с точностью 1e-9

## 5. Вывод
Задача успешно решена с использованием MPI и последовательной версии. MPI версия показывает сопоставимую производительность на данном размере данных, накладные расходы на коммуникацию незначительны.

## 6. Инструкция по запуску
```bash
export PPC_NUM_PROC=4
./build/bin/ppc_func_tests --gtest_filter="*VectorAvg*"
23 changes: 23 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/seq/include/ops_seq.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "../../../nazyrov_a_a_vector_avg/common/include/common.hpp"

namespace nazyrov_a_a_vector_avg {

class VectorAvgSEQ : public BaseTask {
public:
explicit VectorAvgSEQ(const InType &in);
~VectorAvgSEQ() override = default;

static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kSEQ;
}

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

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

#include <numeric>

namespace nazyrov_a_a_vector_avg {

VectorAvgSEQ::VectorAvgSEQ(const InType &in) : BaseTask() {
GetInput() = in;
}

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

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

bool VectorAvgSEQ::RunImpl() {
const auto &input = GetInput();
double sum = std::accumulate(input.begin(), input.end(), 0.0);
GetOutput() = sum / static_cast<double>(input.size());
return true;
}

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

} // namespace nazyrov_a_a_vector_avg
7 changes: 7 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"tasks": {
"mpi": "enabled",
"seq": "enabled"
},
"tasks_type": "processes"
}
14 changes: 14 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/tests/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
InheritParentConfig: true

Checks: >
-modernize-loop-convert,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-non-const-global-variables,
-misc-override-with-different-visibility,
-misc-use-anonymous-namespace,
-modernize-use-std-print,
-modernize-type-traits

CheckOptions:
- key: readability-function-cognitive-complexity.Threshold
value: 50 # Relaxed for tests
82 changes: 82 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/tests/functional/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <gtest/gtest.h>
#include <mpi.h>

#include <numeric>
#include <random>
#include <vector>

#include "../../common/include/common.hpp"
#include "../../mpi/include/ops_mpi.hpp"
#include "../../seq/include/ops_seq.hpp"

namespace nazyrov_a_a_vector_avg {

class NazyrovVectorAvgTest : public ::testing::TestWithParam<TestType> {
public:
static void SetUpTestSuite() {
// Инициализация MPI один раз для всех тестов
int initialized = 0;
MPI_Initialized(&initialized);
if (!initialized) {
int argc = 0;
char **argv = nullptr;
MPI_Init(&argc, &argv);
}
}

static void TearDownTestSuite() {
int finalized = 0;
MPI_Finalized(&finalized);
if (!finalized) {
MPI_Finalize();
}
}

protected:
void SetUp() override {
int size = std::get<0>(GetParam());
input_.resize(size);

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 100);

for (int i = 0; i < size; ++i) {
input_[i] = dist(gen);
}

expected_ = std::accumulate(input_.begin(), input_.end(), 0.0) / size;
}

InType input_;
OutType expected_;
};

TEST_P(NazyrovVectorAvgTest, SeqTest) {
auto task = std::make_shared<VectorAvgSEQ>(input_);
ASSERT_TRUE(task->Validation());
ASSERT_TRUE(task->PreProcessing());
ASSERT_TRUE(task->Run());
ASSERT_TRUE(task->PostProcessing());
EXPECT_NEAR(task->GetOutput(), expected_, 1e-9);
}

TEST_P(NazyrovVectorAvgTest, MpiTest) {
auto task = std::make_shared<VectorAvgMPI>(input_);
ASSERT_TRUE(task->Validation());
ASSERT_TRUE(task->PreProcessing());
ASSERT_TRUE(task->Run());
ASSERT_TRUE(task->PostProcessing());

int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
EXPECT_NEAR(task->GetOutput(), expected_, 1e-9);
}
}

INSTANTIATE_TEST_SUITE_P(VectorAvgTests, NazyrovVectorAvgTest,
::testing::Values(std::make_tuple(10, "small"), std::make_tuple(100, "medium"),
std::make_tuple(1000, "large")));

} // namespace nazyrov_a_a_vector_avg
68 changes: 68 additions & 0 deletions tasks/nazyrov_a_a_vector_avg/tests/performance/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <gtest/gtest.h>
#include <mpi.h>

#include <numeric>
#include <random>
#include <vector>

#include "../../common/include/common.hpp"
#include "../../mpi/include/ops_mpi.hpp"
#include "../../seq/include/ops_seq.hpp"

namespace nazyrov_a_a_vector_avg {

TEST(NazyrovVectorAvgPerfTest, SeqPerformance) {
const int size = 1000000;
InType input(size);

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 100);
for (int i = 0; i < size; ++i) {
input[i] = dist(gen);
}

auto task = std::make_shared<VectorAvgSEQ>(input);

auto start = std::chrono::high_resolution_clock::now();
ASSERT_TRUE(task->Validation());
ASSERT_TRUE(task->PreProcessing());
ASSERT_TRUE(task->Run());
ASSERT_TRUE(task->PostProcessing());
auto end = std::chrono::high_resolution_clock::now();

double elapsed = std::chrono::duration<double>(end - start).count();
std::cout << "SEQ time for " << size << " elements: " << elapsed << "s\n";
EXPECT_GT(elapsed, 0);
}

TEST(NazyrovVectorAvgPerfTest, MpiPerformance) {
const int size = 1000000;
InType input(size);

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dist(1, 100);
for (int i = 0; i < size; ++i) {
input[i] = dist(gen);
}

auto task = std::make_shared<VectorAvgMPI>(input);

auto start = std::chrono::high_resolution_clock::now();
ASSERT_TRUE(task->Validation());
ASSERT_TRUE(task->PreProcessing());
ASSERT_TRUE(task->Run());
ASSERT_TRUE(task->PostProcessing());
auto end = std::chrono::high_resolution_clock::now();

int rank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
double elapsed = std::chrono::duration<double>(end - start).count();
std::cout << "MPI time for " << size << " elements: " << elapsed << "s\n";
EXPECT_GT(elapsed, 0);
}
}

} // namespace nazyrov_a_a_vector_avg