diff --git a/tasks/muhammadkhon_i_max_matrix_elem/common/include/common.hpp b/tasks/muhammadkhon_i_max_matrix_elem/common/include/common.hpp new file mode 100644 index 0000000000..2a1f019ee6 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/common/include/common.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +struct Matrix { + std::vector data; + int rows; + int columns; +}; + +using InType = Matrix; +using OutType = int; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/info.json b/tasks/muhammadkhon_i_max_matrix_elem/info.json new file mode 100644 index 0000000000..becf3d7d7e --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Исрам", + "last_name": "Мухаммадхон", + "middle_name": "Абдулманон", + "group_number": "3823Б1ПР5", + "task_number": "13" + } +} diff --git a/tasks/muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp b/tasks/muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..26a70d6ee2 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +class MuhammadkhonIMaxMatrixElemMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit MuhammadkhonIMaxMatrixElemMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/mpi/src/ops_mpi.cpp b/tasks/muhammadkhon_i_max_matrix_elem/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..1c4f3e8879 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/mpi/src/ops_mpi.cpp @@ -0,0 +1,87 @@ +#include "muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +MuhammadkhonIMaxMatrixElemMPI::MuhammadkhonIMaxMatrixElemMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool MuhammadkhonIMaxMatrixElemMPI::ValidationImpl() { + const Matrix &inputdata = GetInput(); + const int rows = inputdata.rows; + const int columns = inputdata.columns; + const std::vector &matrix = inputdata.data; + + return !matrix.empty() && rows > 0 && columns > 0 && + matrix.size() == static_cast(rows) * static_cast(columns); +} + +bool MuhammadkhonIMaxMatrixElemMPI::PreProcessingImpl() { + return true; +} + +bool MuhammadkhonIMaxMatrixElemMPI::RunImpl() { + const Matrix &inputdata = GetInput(); + const int rows = inputdata.rows; + const int columns = inputdata.columns; + const std::vector &matrix = inputdata.data; + + if (matrix.empty()) { + GetOutput() = 0; + return true; + } + + int rank = 0; + int size = 1; + MPI_Comm_size(MPI_COMM_WORLD, &size); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + const int rows_at_one_process = rows / size; + const int remaining_rows = rows % size; + std::vector counts_per_process(size, rows_at_one_process * columns); + std::vector displacements(size, 0); + + if (remaining_rows != 0) { + for (int process_index = 0; process_index < remaining_rows; ++process_index) { + counts_per_process[process_index] += columns; + } + } + for (int process_index = 1; process_index < size; ++process_index) { + displacements[process_index] = displacements[process_index - 1] + counts_per_process[process_index - 1]; + } + + std::vector recvbuf(counts_per_process[rank]); + MPI_Scatterv(matrix.data(), counts_per_process.data(), displacements.data(), MPI_INT, recvbuf.data(), + counts_per_process[rank], MPI_INT, 0, MPI_COMM_WORLD); + + int local_max = std::numeric_limits::min(); + if (!recvbuf.empty()) { + local_max = recvbuf[0]; + for (size_t index = 1; index < recvbuf.size(); ++index) { + local_max = std::max(local_max, recvbuf[index]); + } + } + + int global_max = 0; + MPI_Allreduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + + GetOutput() = global_max; + return true; +} + +bool MuhammadkhonIMaxMatrixElemMPI::PostProcessingImpl() { + return true; +} + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/report.md b/tasks/muhammadkhon_i_max_matrix_elem/report.md new file mode 100644 index 0000000000..c675204817 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/report.md @@ -0,0 +1,151 @@ +- Student: <Мухаммадхон Исрам Абдулманон>, group <3823Б1ПР5> +- Technology: +- Variant: <13> + +## 1. Introduction +МОТИВАЦИЯ -> Ускорение поиска максимально элемента в матрицах большого размера,за счёт распределения нагрузки по нескольким процесам +ПРОБЛЕМА -> Матрицы большого размера могут очень долго обрабатываться +РЕЗУЛЬТАТ -> Ускорение производительности. + +## 2. Problem Statement +Formal task definition -> для матрицы размером A[a1,b1] найти максимальный элемент +input/output format -> на вход подаётся Matrix(тоесть данные матрицы), на выход подаётся 1 int значение, которое является максимумом в матрице +constraints -> a1,b1 > 0, matrix.size() = a1 * b1. + +## 3. Baseline Algorithm (Sequential) +Describe the base algorithm with enough detail to reproduce. + +```cpp +int max_value = matrix[0]; +for (size_t i = 1; i < matrix.size(); i++) { + max_value = std::max(max_value, matrix[i]); + } + GetOutput() = max_value; +``` + +## 4. Parallelization Scheme +data distribution: +Блочное распределение по строкам +Балансировка нагрузки при неравномерном распределении +, communication pattern: +```cpp +//распределение данных +MPI_Scatterv(matrix.data(), how_many_to_one_proces.data(), offset.data(), MPI_INT, recvbuf.data(), + how_many_to_one_proces[rank], MPI_INT, 0, MPI_COMM_WORLD); +//поиск локального максимума + int local_max; + if (!recvbuf.empty()) { + local_max = recvbuf[0]; + for (size_t i = 1; i < recvbuf.size(); i++) { + local_max = std::max(local_max, recvbuf[i]); + } + } else { + local_max = 0; + } +//поиск глобального максимума + int global_max; + MPI_Allreduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); +``` + rank roles. + Rank 0 - распределение задач + Rank all - локальные вычисления и участие в редукции + + +## 5. Implementation Details +- Code structure (files, key classes/functions) +common - общие структуры данных +mpi - паралельная реализация mpi +seq - последовательная +test - тесты функцианальности и производительности + +```cpp +class MuhammadkhonIMaxMatrixElemSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MuhammadkhonIMaxMatrixElemSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; +``` +```cpp +class MuhammadkhonIMaxMatrixElemMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit MuhammadkhonIMaxMatrixElemMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +``` +```cpp +struct Matrix { + std::vector data; + int rows; + int columns; +}; +``` +- Important assumptions and corner cases + 1- матрица не должна быть пустой + 2- размер матрицы должен соответствовать rows*columns + 3- возращает 1 элемент(максимально значение в матрице) +- Memory usage considerations + 1- в mpi каждый процесс имеет только свои значения + + +## 6. Experimental Setup +- ОЗУ: 8 Гб +- ОС: Linux Fedora +- Архитектура: x64 + +- Язык программирования: C++ +- Библиотека для параллельного программирования: MPI +- Компилятор MSCV +- Тип сборки: Release + +## 7. Results and Discussion + +### 7.1 Correctness +Размер матрицы был 4000x4000 +Было произведено 4 запуска +В каждом из запусков MPI оказалось эффективнее SEQ + +### 7.2 Performance +Present time, speedup and efficiency. Example table: + +| Mode | processes | AvgTime(s) | Speedup | Efficiency | +|-------------|-----------|------------|---------|------------| +| seq | 1 | 0.09386 | 1.00 | N/A | +| mpi | 2 | 0.06325 | 1.48 | 74.0% | +| mpi | 4 | 0.04038 | 2.32 | 58.0% | +| mpi | 6 | 0.04205 | 2.23 | 37.2% | +| mpi | 8 | 0.03589 | 2.62 | 32.7% | + + +## 8. Conclusions +ВЫВОД :использование mpi показало свою эффективность, ускорив работу в 2.32 раза в среднем +таким образом можно сделать вывод, что распределение нагрузки на несколько процессов является +максимально эффективным метод работы с большим количеством данных +Также можно заметить, что на 6 процессах время выполнения больше чем на 4, это может быть связано +с тем, что распределение проверяемой матрицы получилось не равномерным. +Наибольшая эффективность достигается на 2 процессах, однако максимальное абсолютное ускорение на 8 процессах +Ограничения: если матрицы маленькие, то mpi является неэффективным методом работы +Следовательно использование MPI является эффективным в условиях больших матриц когда комуникация между процессами +не затратит времени больше, чем сама работа с данными + +## 9. References + MICROSOFT MPI - https://learn.microsoft.com/ru-ru/message-passing-interface/microsoft-mpi + Parallel Programming Course - https://learning-process.github.io/parallel_programming_course/ru/index.html + Parallel Programming 2025-2026 - https://disk.yandex.ru/d/NvHFyhOJCQU65w + stack overflow - https://stackoverflow.com/questions diff --git a/tasks/muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp b/tasks/muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..5ea8353183 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +class MuhammadkhonIMaxMatrixElemSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MuhammadkhonIMaxMatrixElemSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/seq/src/ops_seq.cpp b/tasks/muhammadkhon_i_max_matrix_elem/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..c281322482 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/seq/src/ops_seq.cpp @@ -0,0 +1,53 @@ +#include "muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +MuhammadkhonIMaxMatrixElemSEQ::MuhammadkhonIMaxMatrixElemSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = 0; +} + +bool MuhammadkhonIMaxMatrixElemSEQ::ValidationImpl() { + const Matrix &inputdata = GetInput(); + const int rows = inputdata.rows; + const int columns = inputdata.columns; + const std::vector &matrix = inputdata.data; + + return !matrix.empty() && rows > 0 && columns > 0 && + matrix.size() == static_cast(rows) * static_cast(columns); +} + +bool MuhammadkhonIMaxMatrixElemSEQ::PreProcessingImpl() { + return true; +} + +bool MuhammadkhonIMaxMatrixElemSEQ::RunImpl() { + const Matrix &inputdata = GetInput(); + const std::vector &matrix = inputdata.data; + + if (matrix.empty()) { + GetOutput() = 0; + return true; + } + + int max_value = matrix[0]; + for (size_t index = 1; index < matrix.size(); ++index) { + max_value = std::max(max_value, matrix[index]); + } + + GetOutput() = max_value; + return true; +} + +bool MuhammadkhonIMaxMatrixElemSEQ::PostProcessingImpl() { + return true; +} + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/settings.json b/tasks/muhammadkhon_i_max_matrix_elem/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/muhammadkhon_i_max_matrix_elem/tests/.clang-tidy b/tasks/muhammadkhon_i_max_matrix_elem/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/tests/.clang-tidy @@ -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 diff --git a/tasks/muhammadkhon_i_max_matrix_elem/tests/functional/main.cpp b/tasks/muhammadkhon_i_max_matrix_elem/tests/functional/main.cpp new file mode 100644 index 0000000000..3161124d9c --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/tests/functional/main.cpp @@ -0,0 +1,121 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" +#include "muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp" +#include "muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" // ДОБАВЛЕНО: для ppc::util::GTestParamIndex + +namespace muhammadkhon_i_max_matrix_elem { + +class MuhammadkhonIMaxMatrixElemTests : public ppc::util::BaseRunFuncTests { + protected: + void SetUp() override { + auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + test_case_ = std::get<0>(params); + + switch (test_case_) { + case 1: + input_data_.rows = 2; + input_data_.columns = 2; + input_data_.data = {1, 2, 3, 4}; + expected_max_ = 4; + break; + case 2: + input_data_.rows = 3; + input_data_.columns = 2; + input_data_.data = {-1, -5, 8, -3, 0, 7}; + expected_max_ = 8; + break; + case 3: + input_data_.rows = 1; + input_data_.columns = 1; + input_data_.data = {42}; + expected_max_ = 42; + break; + case 4: + input_data_.rows = 2; + input_data_.columns = 3; + input_data_.data = {5, 5, 5, 5, 10, 5}; + expected_max_ = 10; + break; + case 5: + input_data_.rows = 4; + input_data_.columns = 3; + input_data_.data = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + expected_max_ = 0; + break; + case 6: + input_data_.rows = 2; + input_data_.columns = 4; + input_data_.data = {-10, -20, -5, -1, -15, -25, -30, -2}; + expected_max_ = -1; + break; + case 7: + input_data_.rows = 3; + input_data_.columns = 3; + input_data_.data = {100, 200, 150, 300, 250, 350, 400, 450, 500}; + expected_max_ = 500; + break; + case 8: + input_data_.rows = 1; + input_data_.columns = 5; + input_data_.data = {7, 7, 7, 7, 7}; + expected_max_ = 7; + break; + default: + throw std::runtime_error("Unknown test case"); + } + } + + bool CheckTestOutputData(OutType &output_data) final { + return (expected_max_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + public: + static std::string PrintTestParam(const TestType &test_param) { + return "TestCase_" + std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + private: + InType input_data_{}; + int expected_max_ = 0; + int test_case_ = 0; +}; + +namespace { + +TEST_P(MuhammadkhonIMaxMatrixElemTests, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(1, "2x2_matrix"), std::make_tuple(2, "with_negatives"), std::make_tuple(3, "single_element"), + std::make_tuple(4, "repeated_max"), std::make_tuple(5, "all_zeros"), std::make_tuple(6, "all_negatives"), + std::make_tuple(7, "3x3_large_values"), std::make_tuple(8, "1x5_same_values")}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_muhammadkhon_i_max_matrix_elem), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_muhammadkhon_i_max_matrix_elem)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); +const auto kPerfTestName = + MuhammadkhonIMaxMatrixElemTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(MatrixMaxTests, MuhammadkhonIMaxMatrixElemTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace muhammadkhon_i_max_matrix_elem diff --git a/tasks/muhammadkhon_i_max_matrix_elem/tests/performance/main.cpp b/tasks/muhammadkhon_i_max_matrix_elem/tests/performance/main.cpp new file mode 100644 index 0000000000..22c17760ac --- /dev/null +++ b/tasks/muhammadkhon_i_max_matrix_elem/tests/performance/main.cpp @@ -0,0 +1,60 @@ +#include + +#include + +#include "muhammadkhon_i_max_matrix_elem/common/include/common.hpp" +#include "muhammadkhon_i_max_matrix_elem/mpi/include/ops_mpi.hpp" +#include "muhammadkhon_i_max_matrix_elem/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace muhammadkhon_i_max_matrix_elem { + +class MuhammadkhonIMaxMatrixElemPerfTests : public ppc::util::BaseRunPerfTests { + protected: + void SetUp() override { + const int rows = 4000; + const int cols = 4000; + const int matrix_size = rows * cols; + std::vector data(matrix_size); + + for (int index = 0; index < matrix_size; ++index) { + data[index] = (index % 10000) + 1; // 1-10000 + } + + data[0] = 99999; + data[matrix_size - 1] = 99999; + + input_data_.rows = rows; + input_data_.columns = cols; + input_data_.data = data; + expected_max_ = 99999; + } + + bool CheckTestOutputData(OutType &output_data) final { + return (expected_max_ == output_data); + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_{}; + int expected_max_ = 0; +}; + +TEST_P(MuhammadkhonIMaxMatrixElemPerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_muhammadkhon_i_max_matrix_elem); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = MuhammadkhonIMaxMatrixElemPerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, MuhammadkhonIMaxMatrixElemPerfTests, kGtestValues, kPerfTestName); + +} // namespace muhammadkhon_i_max_matrix_elem