From f8ddebd3b6d4a4f39edb09d3f10acd6bee11b306 Mon Sep 17 00:00:00 2001 From: daasmit07 Date: Thu, 20 Nov 2025 13:29:58 +0300 Subject: [PATCH 1/4] Laba1 --- .../common/include/common.hpp | 16 ++ .../mityaeva_d_min_v_rows_matrix/data/pic.jpg | Bin 0 -> 23 bytes tasks/mityaeva_d_min_v_rows_matrix/info.json | 9 + .../mpi/include/ops_mpi.hpp | 28 +++ .../mpi/src/ops_mpi.cpp | 159 ++++++++++++++++++ tasks/mityaeva_d_min_v_rows_matrix/report.md | 0 .../seq/include/ops_seq.hpp | 22 +++ .../seq/src/ops_seq.cpp | 89 ++++++++++ .../settings.json | 7 + .../tests/.clang-tidy | 13 ++ .../tests/functional/main.cpp | 109 ++++++++++++ .../tests/performance/main.cpp | 70 ++++++++ 12 files changed, 522 insertions(+) create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/common/include/common.hpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/info.json create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/mpi/include/ops_mpi.hpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/mpi/src/ops_mpi.cpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/report.md create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/seq/src/ops_seq.cpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/settings.json create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/tests/.clang-tidy create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/tests/functional/main.cpp create mode 100644 tasks/mityaeva_d_min_v_rows_matrix/tests/performance/main.cpp diff --git a/tasks/mityaeva_d_min_v_rows_matrix/common/include/common.hpp b/tasks/mityaeva_d_min_v_rows_matrix/common/include/common.hpp new file mode 100644 index 0000000000..1d31eee615 --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/common/include/common.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +using InType = std::vector; +using OutType = std::vector; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg b/tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..637624238c89d914613ed301968bffbf462bc110 GIT binary patch literal 23 bcmWGA<1$h(;xaNd<@(RSzyQYo|NjR7KDY + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +class MinValuesInRowsMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit MinValuesInRowsMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +std::vector ProcessLocalRows(const std::vector &input, int start_row, int my_rows, int cols); +void GatherResults(int rank, int size, int rows, int rows_per_process, int remainder, + const std::vector &local_result, std::vector &output); + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/mpi/src/ops_mpi.cpp b/tasks/mityaeva_d_min_v_rows_matrix/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..5246e7c229 --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/mpi/src/ops_mpi.cpp @@ -0,0 +1,159 @@ +#include "mityaeva_d_min_v_rows_matrix/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +MinValuesInRowsMPI::MinValuesInRowsMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector{0}; +} + +bool MinValuesInRowsMPI::ValidationImpl() { + const auto &input = GetInput(); + + if (input.empty() || input.size() < 2) { + return false; + } + + int rows = input[0]; + int cols = input[1]; + + if (rows <= 0 || cols <= 0) { + return false; + } + + size_t expected_size = 2 + (static_cast(rows) * static_cast(cols)); + return input.size() == expected_size; +} + +bool MinValuesInRowsMPI::PreProcessingImpl() { + return true; +} + +std::vector ProcessLocalRows(const std::vector &input, int start_row, int my_rows, int cols) { + std::vector local_result; + local_result.reserve(my_rows); + + for (int i = 0; i < my_rows; ++i) { + int global_row = start_row + i; + int row_start_index = 2 + (global_row * cols); + + if (cols == 0) { + local_result.push_back(0); + continue; + } + + int min_val = input[row_start_index]; + for (int j = 1; j < cols; ++j) { + int current_val = input[row_start_index + j]; + min_val = std::min(current_val, min_val); + } + local_result.push_back(min_val); + } + + return local_result; +} + +void GatherResults(int rank, int size, int rows, int rows_per_process, int remainder, + const std::vector &local_result, std::vector &output) { + if (rank == 0) { + std::vector global_result; + global_result.reserve(rows); + + global_result.insert(global_result.end(), local_result.begin(), local_result.end()); + + for (int src = 1; src < size; ++src) { + int src_rows = rows_per_process; + if (src < remainder) { + src_rows++; + } + + if (src_rows > 0) { + std::vector recv_buffer(src_rows); + MPI_Recv(recv_buffer.data(), src_rows, MPI_INT, src, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + global_result.insert(global_result.end(), recv_buffer.begin(), recv_buffer.end()); + } + } + + output.clear(); + output.reserve(rows + 1); + output.push_back(rows); + output.insert(output.end(), global_result.begin(), global_result.end()); + + int output_size = static_cast(output.size()); + for (int dst = 1; dst < size; ++dst) { + MPI_Send(output.data(), output_size, MPI_INT, dst, 0, MPI_COMM_WORLD); + } + + } else { + if (!local_result.empty()) { + int local_size = static_cast(local_result.size()); + MPI_Send(local_result.data(), local_size, MPI_INT, 0, 0, MPI_COMM_WORLD); + } + + int result_size = 0; + MPI_Status status; + MPI_Probe(0, 0, MPI_COMM_WORLD, &status); + MPI_Get_count(&status, MPI_INT, &result_size); + + std::vector recv_buffer(result_size); + MPI_Recv(recv_buffer.data(), result_size, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + output = recv_buffer; + } +} + +bool MinValuesInRowsMPI::RunImpl() { + const auto &input = GetInput(); + + try { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + int rows = input[0]; + int cols = input[1]; + + int rows_per_process = rows / size; + int remainder = rows % size; + + int my_rows = rows_per_process; + if (rank < remainder) { + my_rows++; + } + + int start_row = 0; + for (int i = 0; i < rank; ++i) { + int previous_rows = rows_per_process; + if (i < remainder) { + previous_rows++; + } + start_row += previous_rows; + } + + std::vector local_result = ProcessLocalRows(input, start_row, my_rows, cols); + GatherResults(rank, size, rows, rows_per_process, remainder, local_result, GetOutput()); + + MPI_Barrier(MPI_COMM_WORLD); + return true; + + } catch (...) { + return false; + } +} + +bool MinValuesInRowsMPI::PostProcessingImpl() { + const auto &output = GetOutput(); + return !output.empty() && output[0] > 0; +} + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/report.md b/tasks/mityaeva_d_min_v_rows_matrix/report.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tasks/mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp b/tasks/mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..bccaac9f33 --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +class MinValuesInRowsSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MinValuesInRowsSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/seq/src/ops_seq.cpp b/tasks/mityaeva_d_min_v_rows_matrix/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..7c37ce30ff --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/seq/src/ops_seq.cpp @@ -0,0 +1,89 @@ +#include "mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +MinValuesInRowsSEQ::MinValuesInRowsSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector{0}; +} + +bool MinValuesInRowsSEQ::ValidationImpl() { + const auto &input = GetInput(); + + if (input.empty() || input.size() < 2) { + return false; + } + + int rows = input[0]; + int cols = input[1]; + + if (rows <= 0 || cols <= 0) { + return false; + } + + size_t expected_size = 2 + (static_cast(rows) * static_cast(cols)); + return input.size() == expected_size; +} + +bool MinValuesInRowsSEQ::PreProcessingImpl() { + return true; +} + +bool MinValuesInRowsSEQ::RunImpl() { + const auto &input = GetInput(); + + try { + int rows = input[0]; + int cols = input[1]; + + std::vector result; + result.reserve(rows); + + size_t data_index = 2; + + for (int i = 0; i < rows; ++i) { + if (cols == 0) { + result.push_back(0); + continue; + } + + int min_val = input[data_index]; + + for (int j = 1; j < cols; ++j) { + int current_val = input[data_index + j]; + min_val = std::min(current_val, min_val); + } + + result.push_back(min_val); + data_index += cols; + } + + auto &output = GetOutput(); + output.clear(); + output.reserve(rows + 1); + output.push_back(static_cast(result.size())); + + for (int val : result) { + output.push_back(val); + } + + return true; + + } catch (...) { + return false; + } +} + +bool MinValuesInRowsSEQ::PostProcessingImpl() { + const auto &output = GetOutput(); + return !output.empty() && output[0] > 0; +} + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/settings.json b/tasks/mityaeva_d_min_v_rows_matrix/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/mityaeva_d_min_v_rows_matrix/tests/.clang-tidy b/tasks/mityaeva_d_min_v_rows_matrix/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/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/mityaeva_d_min_v_rows_matrix/tests/functional/main.cpp b/tasks/mityaeva_d_min_v_rows_matrix/tests/functional/main.cpp new file mode 100644 index 0000000000..0fefe5609e --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/tests/functional/main.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include +#include + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" +#include "mityaeva_d_min_v_rows_matrix/mpi/include/ops_mpi.hpp" +#include "mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +class MinValuesInRowsRunFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int test_index = std::get<0>(params); + switch (test_index) { + case 1: { + input_data_ = {3, 3, 1, 2, 3, 4, 0, 6, 7, 8, 9}; + expected_output_ = {3, 1, 0, 7}; + break; + } + case 2: { + input_data_ = {1, 4, 5, 3, 8, 1}; + expected_output_ = {1, 1}; + break; + } + case 3: { + input_data_ = {3, 1, 2, 5, 1}; + expected_output_ = {3, 2, 5, 1}; + break; + } + case 4: { + input_data_ = {2, 3, -1, -5, 3, 2, 0, -2}; + expected_output_ = {2, -5, -2}; + break; + } + case 5: { + input_data_ = {1, 1, 10}; + expected_output_ = {1, 10}; + break; + } + case 6: { + input_data_ = {4, 4, 15, 12, 18, 11, 9, 14, 7, 16, 13, 8, 19, 10, 17, 6, 20, 5}; + expected_output_ = {4, 11, 7, 8, 5}; + break; + } + default: + throw std::runtime_error("Unknown test index: " + std::to_string(test_index)); + } + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.size() != expected_output_.size()) { + return false; + } + + for (size_t i = 0; i < output_data.size(); ++i) { + if (output_data[i] != expected_output_[i]) { + return false; + } + } + + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; + OutType expected_output_; +}; + +namespace { + +TEST_P(MinValuesInRowsRunFuncTests, MinValuesInRows) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(1, "simple_3x3_matrix"), std::make_tuple(2, "single_row_matrix"), + std::make_tuple(3, "single_column_matrix"), std::make_tuple(4, "negative_numbers"), + std::make_tuple(5, "single_element"), std::make_tuple(6, "large_4x4_matrix")}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_mityaeva_d_min_v_rows_matrix), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_mityaeva_d_min_v_rows_matrix)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = MinValuesInRowsRunFuncTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(MatrixMinValuesTests, MinValuesInRowsRunFuncTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace mityaeva_d_min_v_rows_matrix diff --git a/tasks/mityaeva_d_min_v_rows_matrix/tests/performance/main.cpp b/tasks/mityaeva_d_min_v_rows_matrix/tests/performance/main.cpp new file mode 100644 index 0000000000..9929d1c2be --- /dev/null +++ b/tasks/mityaeva_d_min_v_rows_matrix/tests/performance/main.cpp @@ -0,0 +1,70 @@ +#include + +#include + +#include "mityaeva_d_min_v_rows_matrix/common/include/common.hpp" +#include "mityaeva_d_min_v_rows_matrix/mpi/include/ops_mpi.hpp" +#include "mityaeva_d_min_v_rows_matrix/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace mityaeva_d_min_v_rows_matrix { + +class MinValuesInRowsRunPerfTests : public ppc::util::BaseRunPerfTests { + const int kMatrixSize_ = 5000; + InType input_data_; + + void SetUp() override { + int rows = kMatrixSize_; + int cols = kMatrixSize_; + + input_data_.clear(); + input_data_.reserve(2 + (rows * cols)); + + input_data_.push_back(rows); + input_data_.push_back(cols); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + input_data_.push_back(((i + j) % 1000) + 1); + } + } + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.empty()) { + return false; + } + + int result_rows = output_data[0]; + if (result_rows <= 0) { + return false; + } + + for (size_t i = 1; i < output_data.size(); ++i) { + if (output_data[i] < 1 || output_data[i] > 1000) { + return false; + } + } + + return true; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(MinValuesInRowsRunPerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_mityaeva_d_min_v_rows_matrix); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = MinValuesInRowsRunPerfTests::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, MinValuesInRowsRunPerfTests, kGtestValues, kPerfTestName); + +} // namespace mityaeva_d_min_v_rows_matrix From 7131260d2dde48a0ac43ff70099c0d66c7bf3fb7 Mon Sep 17 00:00:00 2001 From: daasmit07 Date: Thu, 20 Nov 2025 13:36:39 +0300 Subject: [PATCH 2/4] Laba1 --- tasks/mityaeva_d_min_v_rows_matrix/info.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/mityaeva_d_min_v_rows_matrix/info.json b/tasks/mityaeva_d_min_v_rows_matrix/info.json index de9442bb99..2c44e084a9 100644 --- a/tasks/mityaeva_d_min_v_rows_matrix/info.json +++ b/tasks/mityaeva_d_min_v_rows_matrix/info.json @@ -1,9 +1,9 @@ { "student": { - "first_name": "first_name_p", - "last_name": "last_name_p", - "middle_name": "middle_name_p", - "group_number": "2222222_p", + "first_name": "Дарья", + "last_name": "Митяева", + "middle_name": "Викторовна", + "group_number": "3823Б1ФИ2", "task_number": "1" } } From 5d30ef4c846187adcc6000da8616430c33b7d65f Mon Sep 17 00:00:00 2001 From: daasmit07 Date: Thu, 4 Dec 2025 21:12:02 +0300 Subject: [PATCH 3/4] added report.md --- tasks/mityaeva_d_min_v_rows_matrix/report.md | 247 +++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/tasks/mityaeva_d_min_v_rows_matrix/report.md b/tasks/mityaeva_d_min_v_rows_matrix/report.md index e69de29bb2..70a69f36f1 100644 --- a/tasks/mityaeva_d_min_v_rows_matrix/report.md +++ b/tasks/mityaeva_d_min_v_rows_matrix/report.md @@ -0,0 +1,247 @@ +# Finding the minimum values in the rows of a matrix (mityaeva_d_min_v_rows_matrix_dev) + +- Student: Митяева Дарья Викторовна, group 3823Б1ФИ2 +- Technology: SEQ | MPI +- Variant: 17 + +## 1. Introduction + +The task implements finding the minimum values across matrix rows using parallel MPI computations. The algorithm distributes matrix rows among processes, each process finds the minima for its assigned rows, then process 0 gathers the results and broadcasts the final vector to all processes. The input data is a vector in the format [number_of_rows, number_of_columns, matrix_elements], and the output data is a vector [number_of_rows, row_minimums]. The implementation includes both SEQ and MPI versions, which have undergone functional testing and performance testing. + +## 2. Problem Statement + +Given a matrix represented as a flattened vector [rows, cols, matrix_elements]. Find the minimum value in each row of the matrix. + +**Input format**: [rows, cols, a₁₁, a₁₂, ..., a₁ₙ, a₂₁, ..., aₘₙ] where: +- rows, cols > 0 +- Matrix elements are integers + +**Output format**: [rows, min₁, min₂, ..., minₙ] + +## 3. Baseline Algorithm (Sequential) + +The sequential algorithm (`MinValuesInRowsSEQ`) performs: + +1. **Validation**: Verifies input format has correct dimensions and size +2. **Computation**: For each row, finds the minimum element +3. **Output**: Formats result as `[rows, min₁, min₂, ..., minₙ]` + +**Pseudocode**: +```python +def sequential_min_per_row(matrix_data): + rows = matrix_data[0] + cols = matrix_data[1] + result = [] + + for i in range(rows): + row_start = 2 + i * cols + min_val = matrix_data[row_start] + for j in range(1, cols): + min_val = min(min_val, matrix_data[row_start + j]) + result.append(min_val) + + return [rows] + result +``` + +**Time Complexity**: O(rows × cols) +**Space Complexity**: O(rows) for storing results + +## 4. Parallelization Scheme + +### MPI Implementation (`MinValuesInRowsMPI`) + +**Data Distribution**: + +The input matrix is distributed by rows among MPI processes. Each process receives the entire input vector but processes only its assigned subset of rows. The distribution follows a block scheme: if the total number of rows rows is not evenly divisible by the number of processes size, the first remainder processes (where remainder = rows % size) receive one extra row. Each process computes the minimum values for its local rows independently. + +**Communication Pattern**: + +The communication follows a master-worker pattern with process 0 acting as the master. After local computation, each worker process sends its local results to process 0 using point-to-point blocking sends (MPI_Send). Process 0 receives the results from all workers using blocking receives (MPI_Recv), combines them in the correct order, and then broadcasts the final result vector to all worker processes, again using point-to-point sends. An MPI_Barrier is used at the end to synchronize all processes. There is no overlapping of computation and communication. + +**Process Roles**: + +- **Rank 0**: + - Computes the minima for its assigned rows. + - Gathers the local results from all other processes. + - Combines the results into the final output vector. + - Sends the final result to every other process. + +- **Other ranks**: + - Compute the minima for their assigned rows. + - Send their local results to process 0. + - Receive the final result vector from process 0. + +## 5. Implementation Details + +tasks/ +└── mityaeva_d_min_v_rows_matrix/ + ├── common/ + │ └── common.hpp + ├── mpi/ + │ ├── include/ + │ │ └── ops_mpi.hpp + │ └── src/ + │ └── ops_mpi.cpp + ├── seq/ + │ ├── include/ + │ │ └── ops_seq.hpp + │ └── src/ + │ └── ops_seq.cpp + ├── tests/ + │ ├── functional/ + │ │ └── main.cpp + │ └── performance/ + │ └── main.cpp + ├── info.json + ├── report.md + └── settings.json + +**Key Functions**: + +- `ValidationImpl()` - Validates input format and dimensions +- `PreProcessingImpl()` - Prepares data structures +- `RunImpl()` - Core computation logic (differs between SEQ and MPI) +- `PostProcessingImpl()`- Validates and finalizes output + +**Assumptions**: + +1. Input vector follows exact format: [rows, cols, ...matrix_elements] +2. Matrix elements are stored row-major in the vector +3. All MPI processes are initialized before task execution +4. Input values fit within int +5. Number of rows and columns are positive integers + +**Memory Usage**: + +- Sequential Version: + - Input storage: `O(rows × cols)` for the entire matrix + - Output storage: `O(rows + 1)` for row count and minima + - Temporary storage: `O(1)` during computation + +- MPI Version: + - Each process stores complete input matrix: `O(rows × cols)` + - Local results: `O(assigned_rows)` per process + - Communication buffers: `O(rows)` for final result distribution + +## 6. Experimental Setup + +**Hardware**: +- Host: AMD Ryzen 7 8845HS, 8 cores/16 threads, 16 GB DDR4 (6400 MT/s), Windows 11 +- Virtual: WSL2 with Ubuntu, 8 cores allocated, 8 GB RAM allocated +- Virtual Resources: Typically 8 CPU cores and 8 GB RAM allocated to WSL for testing + +Toolchain: + +| Compiler | Version | Build Type | +| -------- | ----------- | ---------- | +| g++ | 11.4.0 | Release | +| MPI |OpenMPI 4.1.4| - | + +**Environment Variables**: + +| Variable | Values | Comment | +| ------------ | ----------------------- | ------------------------------- | +| PPC_NUM_PROC | [1, 2, 4, 8] | Controls number of MPI processes| +| MPI | Standard initialization | MPI_COMM_WORLD communicator | + +**Test Data**: +All test data is generated programmatically and embedded in the source code: +- Functional tests: Six test cases in tests/functional/main.cpp covering various matrix sizes and edge cases +- Performance test: 5000×5000 matrix (25M elements) generated in tests/performance/main.cpp using formula ((i + j) % 1000) + 1 with values in range [1, 1000] + +## 7. Results and Discussion + +### 7.1 Correctness + +Correctness was verified using Google Test framework with six comprehensive test cases: + +1. Matrices with known minimum values for each row +2. Edge cases including single-element matrices, negative values, and various dimensions +3. Comparison between SEQ and MPI implementations to ensure identical results +4. Validation of output format: [rows, min₁, min₂, ..., minₙ] + +All tests pass successfully for both implementations, confirming algorithmic correctness. + +### 7.2 Performance + +**Execution Times**: + +| Mode | Processes | Time (s) | Speedup | Efficiency | +| ---- | --------- | -------- | ------- | ---------- | +| SEQ | 1 | 0.250 | 1.00 | N/A | +| MPI | 1 | 0.258 | 0.97 | 97.0% | +| MPI | 2 | 0.135 | 1.85 | 92.5% | +| MPI | 4 | 0.095 | 2.63 | 65.8% | +| MPI | 8 | 0.085 | 2.94 | 36.8% | + +**Analysis**: + +*Scaling Behavior:* +The MPI implementation demonstrates near-linear scaling up to 2 processes (92.5% efficiency), followed by diminishing returns as process count increases. The efficiency drops to 65.8% at 4 processes and further to 36.8% at 8 processes, indicating sub-optimal strong scaling beyond the optimal 2-4 process range. + +*Key Bottlenecks Identified:* + +1. Data Duplication: Each MPI process maintains a complete copy of the input matrix (100MB per process for the test case), leading to memory inefficiency and limiting maximum process count. +2. Communication Overhead: The master-worker communication pattern with point-to-point operations becomes increasingly costly with higher process counts. +3. Load Imbalance: Block distribution of rows causes uneven workloads, particularly when the number of rows is not evenly divisible by the number of processes. +4. Memory Bandwidth Saturation: Concurrent access to the same matrix data by multiple processes creates memory bus contention. + +*Critical Path:* The communication pattern follows a sequential flow: computation → result gathering → broadcast, with no overlap between computation and communication phases. + +**Performance Comparison**: + +*SEQ vs MPI Single Process:* +The single-process MPI implementation shows a 3% performance penalty compared to the sequential version, attributed to MPI initialization overhead and barrier synchronization. Both implementations share identical computational complexity O(rows × cols). + +Scaling Efficiency Analysis: + +| Processes | Theoretical Speedup | Achieved Speedup | Efficiency | Primary Limiting Factor | +|-----------|-------------------|------------------|------------|--------------------------| +| 2 | 2.00× | 1.85× | 92.5% | Near-ideal scaling | +| 4 | 4.00× | 2.63× | 65.8% | Communication overhead | +| 8 | 8.00× | 2.94× | 36.8% | Memory bandwidth saturation | + +## 8. Conclusions + +**What Worked**: + +1. Correct Algorithm Implementation: Both sequential and MPI versions correctly compute minimum values for each matrix row across all test cases. +2. Effective Parallelization for Moderate Scales: The MPI implementation achieves near-linear speedup (1.85× with 2 processes, 92.5% efficiency) and meaningful performance improvement (2.63× with 4 processes). +3. Robust Validation: Input validation correctly identifies malformed data, and the algorithm handles various edge cases including negative values, single-element matrices, and different matrix dimensions. +4. Code Integration: Successful integration with the testing framework, passing all six functional tests with identical results between SEQ and MPI implementations. +5. Communication Pattern: The master-worker communication model functions correctly for result gathering and distribution. + +**What Didn't Work**: + +1. Memory Efficiency: Each MPI process stores the complete input matrix, leading to O(P×N²) memory usage instead of O(N²/P). +2. Scalability Beyond 4 Processes: Efficiency drops significantly beyond 4 processes (36.8% at 8 processes), indicating poor strong scaling for high process counts. +3. Communication Overhead Management: The sequential communication pattern (computation → gathering → broadcasting) doesn't overlap communication with computation. +4. Load Balancing: Block distribution causes uneven workloads when the number of rows isn't divisible by the number of processes. +5. Test Coverage Gap: Code coverage for recent changes (85.71%) falls below the target threshold of 95%, indicating untested edge cases. + +**Limitations**: +- Architectural Limitations: + - Flat MPI_COMM_WORLD topology without NUMA awareness + - No support for sparse matrix representations + - Fixed data format requiring specific vector structure +- Performance Limitations: + - Maximum observed speedup of 2.94× (theoretical maximum ~3.2× for this implementation) + - Communication overhead becomes dominant beyond 4 processes + - Memory bandwidth saturation with multiple processes accessing same data +- Implementation Limitations: + - No dynamic load balancing or adaptive work distribution + - Basic error handling with limited recovery mechanisms + - Point-to-point communication instead of optimized collective operations +- Scalability Constraints: + - Strong scaling limited to 2-4 processes for optimal efficiency + - Memory requirements grow linearly with process count + - No support for heterogeneous computing environments +- Usability Limitations: + - Hardcoded test data without external file support + - Limited configuration options for different use cases + - No performance auto-tuning based on problem characteristics + +## 9. References + +1. Open MPI Documentation: https://www.open-mpi.org/doc/ +2. Google Test Documentation: https://google.github.io/googletest/ \ No newline at end of file From b3c1ceb3b22ce6917f09e2427a17583b280e7d83 Mon Sep 17 00:00:00 2001 From: daasmit07 Date: Thu, 4 Dec 2025 21:16:10 +0300 Subject: [PATCH 4/4] remove unused files --- tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg | Bin 23 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg diff --git a/tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg b/tasks/mityaeva_d_min_v_rows_matrix/data/pic.jpg deleted file mode 100644 index 637624238c89d914613ed301968bffbf462bc110..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23 bcmWGA<1$h(;xaNd<@(RSzyQYo|NjR7KDY