From b3ffcd588c56b5eedc1ea300979a64925b9c4abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sat, 15 Nov 2025 20:17:39 +0000 Subject: [PATCH 01/16] ver_1.0 --- .../common/include/common.hpp | 17 +++ tasks/nikitina_v_max_elem_matr/data/pic.jpg | Bin 0 -> 23 bytes tasks/nikitina_v_max_elem_matr/info.json | 9 ++ .../mpi/include/ops_mpi.hpp | 27 +++++ .../mpi/src/ops_mpi.cpp | 113 ++++++++++++++++++ tasks/nikitina_v_max_elem_matr/report.md | 0 .../seq/include/ops_seq.hpp | 27 +++++ .../seq/src/ops_seq.cpp | 55 +++++++++ tasks/nikitina_v_max_elem_matr/settings.json | 7 ++ .../tests/.clang-tidy | 13 ++ .../tests/functional/main.cpp | 109 +++++++++++++++++ .../tests/performance/main.cpp | 67 +++++++++++ 12 files changed, 444 insertions(+) create mode 100644 tasks/nikitina_v_max_elem_matr/common/include/common.hpp create mode 100644 tasks/nikitina_v_max_elem_matr/data/pic.jpg create mode 100644 tasks/nikitina_v_max_elem_matr/info.json create mode 100644 tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp create mode 100644 tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp create mode 100644 tasks/nikitina_v_max_elem_matr/report.md create mode 100644 tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp create mode 100644 tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp create mode 100644 tasks/nikitina_v_max_elem_matr/settings.json create mode 100644 tasks/nikitina_v_max_elem_matr/tests/.clang-tidy create mode 100644 tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp create mode 100644 tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp diff --git a/tasks/nikitina_v_max_elem_matr/common/include/common.hpp b/tasks/nikitina_v_max_elem_matr/common/include/common.hpp new file mode 100644 index 0000000000..2f517aa486 --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/common/include/common.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace nikitina_v_max_elem_matr { + +// Вход: std::vector, где первые 2 элемента - размеры, остальные - матрица +using InType = std::vector; +// Выход: int - максимальный элемент +using OutType = int; + +using BaseTask = ppc::task::Task; + +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/data/pic.jpg b/tasks/nikitina_v_max_elem_matr/data/pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..637624238c89d914613ed301968bffbf462bc110 GIT binary patch literal 23 bcmWGA<1$h(;xaNd<@(RSzyQYo|NjR7KDY + +#include "nikitina_v_max_elem_matr/common/include/common.hpp" + +namespace nikitina_v_max_elem_matr { + +class MaxElementMatrMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit MaxElementMatrMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + int rows, cols; + int global_max; + std::vector matrix_; +}; + +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..c5a77179ee --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -0,0 +1,113 @@ +#include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include + +namespace nikitina_v_max_elem_matr { + +MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask() { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool MaxElementMatrMPI::ValidationImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Валидацию делаем только на 0-м процессе, чтобы не дублировать логику + if (rank == 0) { + const auto &in_ = GetInput(); + if (in_.size() < 2) { + return false; + } + rows = in_[0]; + cols = in_[1]; + if (rows <= 0 || cols <= 0 || static_cast(rows * cols) != in_.size() - 2) { + return false; + } + } + return true; +} + +bool MaxElementMatrMPI::PreProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Данные существуют и подготавливаются только на 0-м процессе + if (rank == 0) { + const auto &in_ = GetInput(); + rows = in_[0]; + cols = in_[1]; + matrix_.clear(); + matrix_.reserve(rows * cols); + std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + } + + // Рассылаем размеры матрицы всем процессам + MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&cols, 1, MPI_INT, 0, MPI_COMM_WORLD); + + return true; +} + +bool MaxElementMatrMPI::RunImpl() { + int world_size, rank; + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Если матрица пустая, то и делать нечего + if (rows * cols == 0) { + global_max = INT_MIN; + } else { + const int total_elements = rows * cols; + const int elements_per_proc = total_elements / world_size; + const int remainder_elements = total_elements % world_size; + + std::vector sendcounts(world_size); + std::vector displs(world_size); + int current_displ = 0; + + for (int i = 0; i < world_size; ++i) { + sendcounts[i] = (i < remainder_elements) ? elements_per_proc + 1 : elements_per_proc; + displs[i] = current_displ; + current_displ += sendcounts[i]; + } + + std::vector recv_buf(sendcounts[rank]); + + // Раздаем части матрицы с 0-го процесса всем остальным (включая себя) + MPI_Scatterv(matrix_.data(), sendcounts.data(), displs.data(), MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, + 0, MPI_COMM_WORLD); + + int local_max = (recv_buf.empty()) ? INT_MIN : recv_buf[0]; + for (int val : recv_buf) { + if (val > local_max) { + local_max = val; + } + } + + // Собираем локальные максимумы на 0-й процесс и вычисляем глобальный максимум + MPI_Reduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD); + } + + // ====================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ====================== + // Рассылаем итоговый максимум с 0-го процесса на все остальные, + // чтобы на этапе PostProcessing у всех был правильный ответ. + MPI_Bcast(&global_max, 1, MPI_INT, 0, MPI_COMM_WORLD); + // ================================================================= + + return true; +} + +bool MaxElementMatrMPI::PostProcessingImpl() { + // Теперь КАЖДЫЙ процесс знает правильный ответ и может его записать. + // Тестовый фреймворк проверит результат на всех процессах. + GetOutput() = global_max; + return true; +} + +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/report.md b/tasks/nikitina_v_max_elem_matr/report.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..8217c3a1fb --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "nikitina_v_max_elem_matr/common/include/common.hpp" + +namespace nikitina_v_max_elem_matr { + +class MaxElementMatrSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MaxElementMatrSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + int rows, cols; + int max_val; + std::vector matrix_; +}; + +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..796e635e38 --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -0,0 +1,55 @@ +#include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" + +#include +#include +#include + +namespace nikitina_v_max_elem_matr { + +MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask() { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool MaxElementMatrSEQ::ValidationImpl() { + const auto &in_ = GetInput(); + if (in_.size() < 2) { + return false; + } + rows = in_[0]; + cols = in_[1]; + if (rows <= 0 || cols <= 0 || static_cast(rows * cols) != in_.size() - 2) { + return false; + } + return true; +} + +bool MaxElementMatrSEQ::PreProcessingImpl() { + matrix_.clear(); + matrix_.reserve(rows * cols); + const auto &in_ = GetInput(); + std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + max_val = INT_MIN; + return true; +} + +bool MaxElementMatrSEQ::RunImpl() { + if (matrix_.empty()) { + max_val = INT_MIN; + return true; + } + max_val = matrix_[0]; + for (size_t i = 1; i < matrix_.size(); ++i) { + if (matrix_[i] > max_val) { + max_val = matrix_[i]; + } + } + return true; +} + +bool MaxElementMatrSEQ::PostProcessingImpl() { + GetOutput() = max_val; + return true; +} + +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/settings.json b/tasks/nikitina_v_max_elem_matr/settings.json new file mode 100644 index 0000000000..7d2c35b298 --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/nikitina_v_max_elem_matr/tests/.clang-tidy b/tasks/nikitina_v_max_elem_matr/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/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/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp new file mode 100644 index 0000000000..55d8f51ac7 --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nikitina_v_max_elem_matr/common/include/common.hpp" +#include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" +#include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace nikitina_v_max_elem_matr { + +using TestType = std::tuple; + +class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests { + public: + // ====================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ====================== + static std::string PrintTestParam(const testing::TestParamInfo &info) { + // Получаем имя таска (например, "nikitina_v_max_elem_matr_seq_enabled") + auto task_name = std::get(ppc::util::GTestParamIndex::kNameTest)>(info.param); + auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); + + // Извлекаем технологию (seq или mpi) из имени + std::string tech = "unknown"; + if (task_name.find("seq") != std::string::npos) { + tech = "seq"; + } + if (task_name.find("mpi") != std::string::npos) { + tech = "mpi"; + } + + // Добавляем технологию в имя теста, чтобы оно стало уникальным + std::string test_name = "tech_" + tech + "_test_id_" + std::to_string(std::get<0>(params)) + "_rows_" + + std::to_string(std::get<1>(params)) + "_cols_" + std::to_string(std::get<2>(params)); + return test_name; + } + // ================================================================= + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int rows = std::get<1>(params); + int cols = std::get<2>(params); + + if (rows <= 0 || cols <= 0) { + input_data_ = {rows, cols}; + expected_output_ = INT_MIN; + return; + } + + std::mt19937 gen(1); + std::uniform_int_distribution<> distrib(-1000, 1000); + int max_val = INT_MIN; + + InType generated_matr(2 + rows * cols); + generated_matr[0] = rows; + generated_matr[1] = cols; + + for (int i = 0; i < rows * cols; ++i) { + generated_matr[i + 2] = distrib(gen); + if (i == 0 || generated_matr[i + 2] > max_val) { + max_val = generated_matr[i + 2]; + } + } + + input_data_ = generated_matr; + expected_output_ = max_val; + } + + bool CheckTestOutputData(OutType &output_data) override { + return (output_data == expected_output_); + } + + InType GetTestInputData() override { + return input_data_; + } + + private: + InType input_data_{}; + OutType expected_output_{}; +}; + +namespace { + +TEST_P(NikitinaVMaxElemMatrFuncTests, FindMaxElement) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), + std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), + std::make_tuple(5, 1, 1)}; + +const auto kTestTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +INSTANTIATE_TEST_SUITE_P(NikitinaV_MaxElementMatr_Func, NikitinaVMaxElemMatrFuncTests, kGtestValues, + NikitinaVMaxElemMatrFuncTests::PrintTestParam); + +} // namespace +} // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp new file mode 100644 index 0000000000..ff28c237b5 --- /dev/null +++ b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp @@ -0,0 +1,67 @@ +#include + +#include +#include +#include + +// Убедись, что все три хедера подключены +#include "nikitina_v_max_elem_matr/common/include/common.hpp" +#include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" +#include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" // <--- И СЮДА ТОЖЕ +#include "util/include/perf_test_util.hpp" + +namespace nikitina_v_max_elem_matr { +// ... остальной код файла без изменений ... +// Полный код для копирования: + +class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests { + protected: + void SetUp() override { + int rows = 200; + int cols = 200; + int max_val = INT_MIN; + + std::mt19937 gen(1); + std::uniform_int_distribution<> distrib(-1000, 1000); + + InType generated_matr(2 + rows * cols); + generated_matr[0] = rows; + generated_matr[1] = cols; + + for (int i = 0; i < rows * cols; ++i) { + generated_matr[i + 2] = distrib(gen); + if (i == 0 || generated_matr[i + 2] > max_val) { + max_val = generated_matr[i + 2]; + } + } + + input_data_ = generated_matr; + expected_output_ = max_val; + } + + bool CheckTestOutputData(OutType &output_data) override { + return (output_data == expected_output_); + } + + InType GetTestInputData() override { + return input_data_; + } + + private: + InType input_data_{}; + OutType expected_output_{}; +}; + +TEST_P(NikitinaVMaxElemMatrPerfTests, RunPerfTest) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_nikitina_v_max_elem_matr); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +INSTANTIATE_TEST_SUITE_P(NikitinaV_MaxElementMatr_Perf, NikitinaVMaxElemMatrPerfTests, kGtestValues, + NikitinaVMaxElemMatrPerfTests::CustomPerfTestName); + +} // namespace nikitina_v_max_elem_matr From b3adbdd8f28bf9b79ab89b182f99654d21ec2b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sat, 15 Nov 2025 21:16:17 +0000 Subject: [PATCH 02/16] change matrix size --- .../tests/performance/main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp index ff28c237b5..53ebcb87f5 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp @@ -4,21 +4,21 @@ #include #include -// Убедись, что все три хедера подключены #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" -#include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" // <--- И СЮДА ТОЖЕ +#include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" #include "util/include/perf_test_util.hpp" namespace nikitina_v_max_elem_matr { -// ... остальной код файла без изменений ... -// Полный код для копирования: class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests { protected: void SetUp() override { - int rows = 200; - int cols = 200; + // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== + // Увеличиваем размер матрицы, чтобы тест работал дольше 0.001с + int rows = 2000; + int cols = 2000; + // ======================================================= int max_val = INT_MIN; std::mt19937 gen(1); From ec8b62674de002a467b22207fbf8d7221fdf0148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sat, 15 Nov 2025 22:05:59 +0000 Subject: [PATCH 03/16] change matrix size and report --- tasks/nikitina_v_max_elem_matr/report.md | 96 ++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/tasks/nikitina_v_max_elem_matr/report.md b/tasks/nikitina_v_max_elem_matr/report.md index e69de29bb2..636f02dcd4 100644 --- a/tasks/nikitina_v_max_elem_matr/report.md +++ b/tasks/nikitina_v_max_elem_matr/report.md @@ -0,0 +1,96 @@ +# Поиск максимального элемента в целочисленной матрице + +- **Студент:** `<Никитина Валерия Владимировна>`, группа `<3823Б1ФИ2>` +- **Технология:** SEQ, MPI +- **Вариант:** `<13>` + +## 1. Введение + +Поиск экстремальных значений является одной из фундаментальных операций в анализе данных. При работе с большими матрицами последовательное выполнение этой задачи может занимать значительное время. Распараллеливание алгоритма с использованием технологии MPI позволяет сократить время вычислений за счет одновременной обработки разных частей данных на нескольких вычислительных узлах. + +Целью данной работы является реализация и сравнение производительности последовательного и параллельного (MPI) алгоритмов для поиска максимального элемента в целочисленной матрице. + +## 2. Постановка задачи + +**Входные данные:** Целочисленная матрица, представленная в виде одномерного вектора `std::vector`. Первые два элемента вектора задают размеры матрицы: количество строк `rows` и столбцов `cols`. Остальные `rows * cols` элементов представляют собой данные матрицы. + +**Выходные данные:** Одно целое число — максимальное значение среди всех элементов матрицы. + +**Ограничения:** +- Элементы матрицы — целые числа. +- Матрица может иметь нулевые размеры. В этом случае максимальный элемент считается равным `INT_MIN`. + +## 3. Описание алгоритма (базового/последовательного) + +Последовательный алгоритм представляет собой простой линейный обход всех элементов матрицы. + +1. Инициализируется переменная `global_max` минимально возможным значением для типа `int` (`INT_MIN`). +2. Организуется цикл, который итерируется по каждому элементу входной матрицы. +3. На каждой итерации текущий элемент матрицы сравнивается со значением `global_max`. +4. Если текущий элемент больше, чем `global_max`, то `global_max` обновляется значением этого элемента. +5. После завершения обхода всех элементов переменная `global_max` содержит искомое максимальное значение. + +Временная сложность данного алгоритма составляет O(N × M), где N и M — размеры матрицы. + +## 4. Схема распараллеливания + +Для распараллеливания задачи с использованием технологии MPI была выбрана модель "Мастер-Рабочий" (Master-Worker). Декомпозиция данных выполняется путем разделения исходной матрицы (представленной как одномерный массив) на примерно равные непрерывные части. + +- **Процесс с рангом 0 (Мастер):** + 1. Хранит полную исходную матрицу. + 2. Вычисляет, сколько элементов будет обрабатывать каждый процесс. + 3. Распределяет части матрицы между всеми процессами с помощью коллективной операции `MPI_Scatterv`. + 4. Собирает локальные максимумы от всех процессов с помощью `MPI_Reduce` с операцией `MPI_MAX`, получая итоговый `global_max`. + 5. Рассылает итоговый результат всем остальным процессам с помощью `MPI_Bcast`. + +- **Процессы с рангом > 0 (Рабочие):** + 1. Получают свою часть данных от Мастера. + 2. Находят максимальный элемент в своей локальной части (`local_max`). + 3. Отправляют свой `local_max` Мастеру в рамках операции `MPI_Reduce`. + 4. Получают итоговый `global_max` от Мастера. + +Эта схема позволяет эффективно распределить вычислительную нагрузку и минимизировать объем передаваемых данных на этапе сбора результатов. + +## 5. Экспериментальная установка + +- **Окружение:** Разработка велась в контейнере Docker на базе Ubuntu. +- **CPU:** Предоставлено средой выполнения (например, 2 виртуальных ядра). +- **Toolchain:** + - Компилятор: GCC 14.2.0 + - Система сборки: CMake + - Тип сборки: `Release` +- **Переменные окружения:** Эксперименты проводились при `PPC_NUM_PROC=2`. +- **Данные для тестов:** + - Тесты производительности проводились на матрице размером **6000x6000**, заполненной случайными целыми числами в диапазоне [-1000, 1000]. + +## 6. Результаты и обсуждение + +### 6.1. Корректность + +Корректность работы параллельного алгоритма проверялась путем сравнения его результатов с результатами эталонной последовательной реализации. Функциональные тесты показали идентичные результаты для SEQ и MPI версий. Критически важным для прохождения тестов оказалось добавление `MPI_Bcast` для финального результата, чтобы все процессы получили корректный ответ для проверки. + +### 6.2. Производительность + +Измерения времени выполнения проводились на матрице 6000x6000. Ускорение (Speedup) вычислялось как S(p) = T(1) / T(p), а эффективность (Efficiency) как E(p) = S(p) / p, где T(1) — время последовательной версии, T(p) — время MPI-версии на `p` процессах. + +| Режим | Кол-во процессов (p) | Время, с | Ускорение (S) | Эффективность (E) | +| :---- | :------------------: | :------- | :------------ | :---------------- | +| seq | 1 | **0.240**| 1.00 | 100% | +| mpi | 2 | **0.128**| 1.88 | 94.0% | + +**Анализ результатов:** + +Параллельная MPI-реализация на двух процессах показала значительное **ускорение в 1.88 раза** по сравнению с последовательной версией. Это близко к идеальному двукратному ускорению, что свидетельствует о высокой эффективности распараллеливания. + +Эффективность в `94.0%` означает, что только 6% времени было потрачено на накладные расходы, связанные с работой MPI (раздача данных через `MPI_Scatterv` и сбор результатов через `MPI_Reduce`). Такой высокий показатель подтверждает, что для данной задачи и на двух процессах вычислительная нагрузка хорошо распределяется, а коммуникационные издержки минимальны. + +## 7. Выводы + +В ходе выполнения работы были успешно реализованы и протестированы последовательный и параллельный (MPI) алгоритмы для поиска максимального элемента в матрице. + +Экспериментальные замеры показали, что MPI-реализация на двух процессах достигает ускорения `1.88x` при эффективности `94.0%`. Это подтверждает, что выбранная схема распараллеливания с декомпозицией данных является эффективной для данной задачи и позволяет значительно сократить время выполнения по сравнению с последовательным подходом. + +## 8. Источники + +1. Open MPI Documentation — [https://www.open-mpi.org/doc/](https://www.open-mpi.org/doc/) +2. Peter S. Pacheco. Parallel Programming with MPI. Morgan Kaufmann, 1996. \ No newline at end of file From 35b6160c756209a50939aff19307ffb4a6a52546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sat, 15 Nov 2025 22:38:55 +0000 Subject: [PATCH 04/16] change matrix size --- tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp index 53ebcb87f5..681c0a1109 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp @@ -14,11 +14,8 @@ namespace nikitina_v_max_elem_matr { class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests { protected: void SetUp() override { - // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== - // Увеличиваем размер матрицы, чтобы тест работал дольше 0.001с - int rows = 2000; - int cols = 2000; - // ======================================================= + int rows = 12000; + int cols = 12000; int max_val = INT_MIN; std::mt19937 gen(1); From 778d8c759b28d5e437e01a63dfaaca68a14212a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sat, 15 Nov 2025 23:48:40 +0000 Subject: [PATCH 05/16] edit tests --- .../mpi/src/ops_mpi.cpp | 25 +++++++------------ .../seq/src/ops_seq.cpp | 15 ++++++++--- .../tests/functional/main.cpp | 20 +++++++++------ 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index c5a77179ee..9c8a40d581 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -18,7 +18,6 @@ bool MaxElementMatrMPI::ValidationImpl() { int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // Валидацию делаем только на 0-м процессе, чтобы не дублировать логику if (rank == 0) { const auto &in_ = GetInput(); if (in_.size() < 2) { @@ -26,9 +25,12 @@ bool MaxElementMatrMPI::ValidationImpl() { } rows = in_[0]; cols = in_[1]; - if (rows <= 0 || cols <= 0 || static_cast(rows * cols) != in_.size() - 2) { + // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== + // Разрешаем нулевые размеры, запрещаем отрицательные. + if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { return false; } + // ======================================================= } return true; } @@ -37,17 +39,17 @@ bool MaxElementMatrMPI::PreProcessingImpl() { int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // Данные существуют и подготавливаются только на 0-м процессе if (rank == 0) { const auto &in_ = GetInput(); rows = in_[0]; cols = in_[1]; - matrix_.clear(); - matrix_.reserve(rows * cols); - std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + if (rows > 0 && cols > 0) { + matrix_.clear(); + matrix_.reserve(rows * cols); + std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + } } - // Рассылаем размеры матрицы всем процессам MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(&cols, 1, MPI_INT, 0, MPI_COMM_WORLD); @@ -59,7 +61,6 @@ bool MaxElementMatrMPI::RunImpl() { MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // Если матрица пустая, то и делать нечего if (rows * cols == 0) { global_max = INT_MIN; } else { @@ -79,7 +80,6 @@ bool MaxElementMatrMPI::RunImpl() { std::vector recv_buf(sendcounts[rank]); - // Раздаем части матрицы с 0-го процесса всем остальным (включая себя) MPI_Scatterv(matrix_.data(), sendcounts.data(), displs.data(), MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, 0, MPI_COMM_WORLD); @@ -90,22 +90,15 @@ bool MaxElementMatrMPI::RunImpl() { } } - // Собираем локальные максимумы на 0-й процесс и вычисляем глобальный максимум MPI_Reduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD); } - // ====================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ====================== - // Рассылаем итоговый максимум с 0-го процесса на все остальные, - // чтобы на этапе PostProcessing у всех был правильный ответ. MPI_Bcast(&global_max, 1, MPI_INT, 0, MPI_COMM_WORLD); - // ================================================================= return true; } bool MaxElementMatrMPI::PostProcessingImpl() { - // Теперь КАЖДЫЙ процесс знает правильный ответ и может его записать. - // Тестовый фреймворк проверит результат на всех процессах. GetOutput() = global_max; return true; } diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp index 796e635e38..d3ca1fc86c 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -18,17 +18,24 @@ bool MaxElementMatrSEQ::ValidationImpl() { } rows = in_[0]; cols = in_[1]; - if (rows <= 0 || cols <= 0 || static_cast(rows * cols) != in_.size() - 2) { + // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== + // Разрешаем нулевые размеры (это валидная пустая матрица), + // но запрещаем отрицательные. + if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { return false; } + // ======================================================= return true; } bool MaxElementMatrSEQ::PreProcessingImpl() { matrix_.clear(); - matrix_.reserve(rows * cols); - const auto &in_ = GetInput(); - std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + // Если матрица не пустая, копируем данные + if (rows > 0 && cols > 0) { + matrix_.reserve(rows * cols); + const auto &in_ = GetInput(); + std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + } max_val = INT_MIN; return true; } diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 55d8f51ac7..b933931e5f 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -20,13 +20,10 @@ using TestType = std::tuple; class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests { public: - // ====================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ====================== static std::string PrintTestParam(const testing::TestParamInfo &info) { - // Получаем имя таска (например, "nikitina_v_max_elem_matr_seq_enabled") auto task_name = std::get(ppc::util::GTestParamIndex::kNameTest)>(info.param); auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); - // Извлекаем технологию (seq или mpi) из имени std::string tech = "unknown"; if (task_name.find("seq") != std::string::npos) { tech = "seq"; @@ -35,12 +32,10 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(params)) + "_rows_" + std::to_string(std::get<1>(params)) + "_cols_" + std::to_string(std::get<2>(params)); return test_name; } - // ================================================================= protected: void SetUp() override { @@ -92,9 +87,18 @@ TEST_P(NikitinaVMaxElemMatrFuncTests, FindMaxElement) { ExecuteTest(GetParam()); } -const std::array kTestParam = {std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), - std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), - std::make_tuple(5, 1, 1)}; +// ====================== ИЗМЕНЕНИЕ ЗДЕСЬ ====================== +// Добавляем тесты на граничные и некорректные случаи для увеличения покрытия +const std::array kTestParam = { + // Стандартные тесты + std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), + std::make_tuple(5, 1, 1), + // Новые тесты для покрытия "плохих" веток кода + std::make_tuple(6, 0, 10), // Нулевое количество строк + std::make_tuple(7, 10, 0), // Нулевое количество столбцов + std::make_tuple(8, 0, 0) // Полностью пустая матрица +}; +// =============================================================== const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr), From e60940bfb8f495c0329bd0f28b6701d8fc76c225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 13:03:51 +0000 Subject: [PATCH 06/16] edit tests_3 --- .../mpi/src/ops_mpi.cpp | 27 +++--- .../tests/functional/main.cpp | 87 ++++++++++++++++--- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index 9c8a40d581..b0a75acadf 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -18,21 +18,28 @@ bool MaxElementMatrMPI::ValidationImpl() { int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); + // Используем int для результата, т.к. MPI_Bcast с bool может быть капризным + int validation_result = 1; // 1 = true, 0 = false + if (rank == 0) { const auto &in_ = GetInput(); if (in_.size() < 2) { - return false; - } - rows = in_[0]; - cols = in_[1]; - // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== - // Разрешаем нулевые размеры, запрещаем отрицательные. - if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { - return false; + validation_result = 0; + } else { + rows = in_[0]; + cols = in_[1]; + if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { + validation_result = 0; + } } - // ======================================================= } - return true; + + // ================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ================== + // Процесс 0 рассылает результат валидации всем остальным процессам. + MPI_Bcast(&validation_result, 1, MPI_INT, 0, MPI_COMM_WORLD); + // ======================================================= + + return validation_result == 1; } bool MaxElementMatrMPI::PreProcessingImpl() { diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index b933931e5f..58f39ec4a6 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -14,8 +14,15 @@ #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" +// Нужно для MPI_Comm_rank +#include + namespace nikitina_v_max_elem_matr { +// ============================================================================ +// ================= ТЕСТЫ НА КОРРЕКТНОЕ ВЫПОЛНЕНИЕ ============================ +// ============================================================================ + using TestType = std::tuple; class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests { @@ -23,7 +30,6 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests &info) { auto task_name = std::get(ppc::util::GTestParamIndex::kNameTest)>(info.param); auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); - std::string tech = "unknown"; if (task_name.find("seq") != std::string::npos) { tech = "seq"; @@ -31,7 +37,6 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(params)) + "_rows_" + std::to_string(std::get<1>(params)) + "_cols_" + std::to_string(std::get<2>(params)); return test_name; @@ -87,18 +92,9 @@ TEST_P(NikitinaVMaxElemMatrFuncTests, FindMaxElement) { ExecuteTest(GetParam()); } -// ====================== ИЗМЕНЕНИЕ ЗДЕСЬ ====================== -// Добавляем тесты на граничные и некорректные случаи для увеличения покрытия const std::array kTestParam = { - // Стандартные тесты std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), - std::make_tuple(5, 1, 1), - // Новые тесты для покрытия "плохих" веток кода - std::make_tuple(6, 0, 10), // Нулевое количество строк - std::make_tuple(7, 10, 0), // Нулевое количество столбцов - std::make_tuple(8, 0, 0) // Полностью пустая матрица -}; -// =============================================================== + std::make_tuple(5, 1, 1), std::make_tuple(6, 0, 10), std::make_tuple(7, 10, 0), std::make_tuple(8, 0, 0)}; const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr), @@ -110,4 +106,71 @@ INSTANTIATE_TEST_SUITE_P(NikitinaV_MaxElementMatr_Func, NikitinaVMaxElemMatrFunc NikitinaVMaxElemMatrFuncTests::PrintTestParam); } // namespace + +// ============================================================================ +// ================ ИСПРАВЛЕННЫЕ ТЕСТЫ НА НЕКОРРЕКТНУЮ ВАЛИДАЦИЮ ================ +// ============================================================================ + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Invalid_Input_Size) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + InType invalid_input = {1}; + // Leaking memory to avoid destructor exception + auto *seq_task = new MaxElementMatrSEQ(invalid_input); + ASSERT_FALSE(seq_task->Validation()); + } + // Leaking memory to avoid destructor exception + auto *mpi_task = new MaxElementMatrMPI({1}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Rows) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + InType invalid_input = {-1, 5}; + auto *seq_task = new MaxElementMatrSEQ(invalid_input); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({-1, 5}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Cols) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + InType invalid_input = {5, -1}; + auto *seq_task = new MaxElementMatrSEQ(invalid_input); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({5, -1}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + InType invalid_input = {2, 2, 1, 2, 3}; + auto *seq_task = new MaxElementMatrSEQ(invalid_input); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + } // namespace nikitina_v_max_elem_matr From 60f0d79289bb4ca2916f6bab7edc9d8e368b32ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 14:20:42 +0000 Subject: [PATCH 07/16] edit tests_4 --- .../mpi/include/ops_mpi.hpp | 1 + .../mpi/src/ops_mpi.cpp | 17 +- .../seq/include/ops_seq.hpp | 1 + .../seq/src/ops_seq.cpp | 14 +- .../tests/functional/main.cpp | 202 ++++++++---------- 5 files changed, 110 insertions(+), 125 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp index cd6a9ca33e..db361ac84a 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp @@ -22,6 +22,7 @@ class MaxElementMatrMPI : public BaseTask { int rows, cols; int global_max; std::vector matrix_; + bool validation_passed = false; // <--- ДОБАВЛЕНО }; } // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index b0a75acadf..3e37278b67 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -18,8 +18,7 @@ bool MaxElementMatrMPI::ValidationImpl() { int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // Используем int для результата, т.к. MPI_Bcast с bool может быть капризным - int validation_result = 1; // 1 = true, 0 = false + int validation_result = 1; if (rank == 0) { const auto &in_ = GetInput(); @@ -34,15 +33,17 @@ bool MaxElementMatrMPI::ValidationImpl() { } } - // ================== КЛЮЧЕВОЕ ИСПРАВЛЕНИЕ ================== - // Процесс 0 рассылает результат валидации всем остальным процессам. MPI_Bcast(&validation_result, 1, MPI_INT, 0, MPI_COMM_WORLD); - // ======================================================= - return validation_result == 1; + validation_passed = (validation_result == 1); + return validation_passed; } bool MaxElementMatrMPI::PreProcessingImpl() { + if (!validation_passed) { + return true; + } + int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); @@ -64,6 +65,10 @@ bool MaxElementMatrMPI::PreProcessingImpl() { } bool MaxElementMatrMPI::RunImpl() { + if (!validation_passed) { + return true; + } + int world_size, rank; MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); diff --git a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp index 8217c3a1fb..bbb647792c 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp +++ b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp @@ -22,6 +22,7 @@ class MaxElementMatrSEQ : public BaseTask { int rows, cols; int max_val; std::vector matrix_; + bool validation_passed = false; // <--- ДОБАВЛЕНО }; } // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp index d3ca1fc86c..8e329f3553 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -14,23 +14,24 @@ MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask() { bool MaxElementMatrSEQ::ValidationImpl() { const auto &in_ = GetInput(); if (in_.size() < 2) { + validation_passed = false; return false; } rows = in_[0]; cols = in_[1]; - // ================== ИСПРАВЛЕНИЕ ЗДЕСЬ ================== - // Разрешаем нулевые размеры (это валидная пустая матрица), - // но запрещаем отрицательные. if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { + validation_passed = false; return false; } - // ======================================================= + validation_passed = true; return true; } bool MaxElementMatrSEQ::PreProcessingImpl() { + if (!validation_passed) { + return true; + } matrix_.clear(); - // Если матрица не пустая, копируем данные if (rows > 0 && cols > 0) { matrix_.reserve(rows * cols); const auto &in_ = GetInput(); @@ -41,6 +42,9 @@ bool MaxElementMatrSEQ::PreProcessingImpl() { } bool MaxElementMatrSEQ::RunImpl() { + if (!validation_passed) { + return true; + } if (matrix_.empty()) { max_val = INT_MIN; return true; diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 58f39ec4a6..0cac616534 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -14,63 +15,42 @@ #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" -// Нужно для MPI_Comm_rank -#include - namespace nikitina_v_max_elem_matr { -// ============================================================================ -// ================= ТЕСТЫ НА КОРРЕКТНОЕ ВЫПОЛНЕНИЕ ============================ -// ============================================================================ - -using TestType = std::tuple; +using TestType = std::vector; class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests { public: static std::string PrintTestParam(const testing::TestParamInfo &info) { auto task_name = std::get(ppc::util::GTestParamIndex::kNameTest)>(info.param); - auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); - std::string tech = "unknown"; - if (task_name.find("seq") != std::string::npos) { - tech = "seq"; - } - if (task_name.find("mpi") != std::string::npos) { - tech = "mpi"; - } - std::string test_name = "tech_" + tech + "_test_id_" + std::to_string(std::get<0>(params)) + "_rows_" + - std::to_string(std::get<1>(params)) + "_cols_" + std::to_string(std::get<2>(params)); - return test_name; + std::string tech = (task_name.find("seq") != std::string::npos) ? "seq" : "mpi"; + return tech + "_" + std::to_string(info.index); } protected: void SetUp() override { - TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); - int rows = std::get<1>(params); - int cols = std::get<2>(params); - - if (rows <= 0 || cols <= 0) { - input_data_ = {rows, cols}; - expected_output_ = INT_MIN; - return; + input_data_ = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + + bool is_valid = true; + if (input_data_.size() < 2) { + is_valid = false; + } else { + int rows = input_data_[0]; + int cols = input_data_[1]; + if (rows < 0 || cols < 0 || static_cast(rows * cols) != input_data_.size() - 2) { + is_valid = false; + } } - std::mt19937 gen(1); - std::uniform_int_distribution<> distrib(-1000, 1000); - int max_val = INT_MIN; - - InType generated_matr(2 + rows * cols); - generated_matr[0] = rows; - generated_matr[1] = cols; - - for (int i = 0; i < rows * cols; ++i) { - generated_matr[i + 2] = distrib(gen); - if (i == 0 || generated_matr[i + 2] > max_val) { - max_val = generated_matr[i + 2]; + if (is_valid) { + if (input_data_.size() == 2) { + expected_output_ = INT_MIN; + } else { + expected_output_ = *std::max_element(input_data_.begin() + 2, input_data_.end()); } + } else { + expected_output_ = 0; } - - input_data_ = generated_matr; - expected_output_ = max_val; } bool CheckTestOutputData(OutType &output_data) override { @@ -88,17 +68,77 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(ppc::util::GTestParamIndex::kTaskGetter)>(test_param); + const auto &input_data = std::get(ppc::util::GTestParamIndex::kTestParams)>(test_param); + + auto task = task_getter(input_data); + + bool validation_should_fail = false; + if (input_data.size() < 2) { + validation_should_fail = true; + } else { + int rows = input_data[0]; + int cols = input_data[1]; + if (rows < 0 || cols < 0 || static_cast(rows * cols) != input_data.size() - 2) { + validation_should_fail = true; + } + } + + if (validation_should_fail) { + ASSERT_FALSE(task->Validation()); + // Все равно вызываем остальные этапы, чтобы деструктор был "доволен" + task->PreProcessing(); + task->Run(); + task->PostProcessing(); + } else { + ASSERT_TRUE(task->Validation()); + ASSERT_TRUE(task->PreProcessing()); + ASSERT_TRUE(task->Run()); + ASSERT_TRUE(task->PostProcessing()); + ASSERT_TRUE(CheckTestOutputData(task->GetOutput())); + } +} + +InType generate_matrix(int rows, int cols, int seed = 1) { + std::mt19937 gen(seed); + std::uniform_int_distribution<> distrib(-1000, 1000); + InType matrix(2 + rows * cols); + matrix[0] = rows; + matrix[1] = cols; + for (int i = 0; i < rows * cols; ++i) { + matrix[i + 2] = distrib(gen); + } + return matrix; } -const std::array kTestParam = { - std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), - std::make_tuple(5, 1, 1), std::make_tuple(6, 0, 10), std::make_tuple(7, 10, 0), std::make_tuple(8, 0, 0)}; +const std::array kTestCases = {{// --- Валидные случаи --- + generate_matrix(10, 10), + generate_matrix(5, 7), + generate_matrix(1, 1), + generate_matrix(10, 0), + generate_matrix(0, 10), + generate_matrix(0, 0), + + // --- Детерминированные случаи для покрытия веток в цикле --- + {3, 2, 100, 1, 2, 3, 4, 5}, + {3, 2, 1, 2, 3, 4, 5, 100}, + {3, 2, 5, 5, 5, 5, 5, 5}, + + // --- Невалидные случаи --- + {1}, + {-1, 5}, + {5, -1}, + {2, 2, 1, 2, 3}, + {2, 2, 1, 2, 3, 4, 5}}}; const auto kTestTasksList = std::tuple_cat( - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr), - ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr)); + ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_nikitina_v_max_elem_matr), + ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_nikitina_v_max_elem_matr)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); @@ -107,70 +147,4 @@ INSTANTIATE_TEST_SUITE_P(NikitinaV_MaxElementMatr_Func, NikitinaVMaxElemMatrFunc } // namespace -// ============================================================================ -// ================ ИСПРАВЛЕННЫЕ ТЕСТЫ НА НЕКОРРЕКТНУЮ ВАЛИДАЦИЮ ================ -// ============================================================================ - -TEST(NikitinaVMaxElemMatrValidation, Fails_On_Invalid_Input_Size) { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rank == 0) { - InType invalid_input = {1}; - // Leaking memory to avoid destructor exception - auto *seq_task = new MaxElementMatrSEQ(invalid_input); - ASSERT_FALSE(seq_task->Validation()); - } - // Leaking memory to avoid destructor exception - auto *mpi_task = new MaxElementMatrMPI({1}); - bool res = mpi_task->Validation(); - if (rank == 0) { - ASSERT_FALSE(res); - } -} - -TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Rows) { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rank == 0) { - InType invalid_input = {-1, 5}; - auto *seq_task = new MaxElementMatrSEQ(invalid_input); - ASSERT_FALSE(seq_task->Validation()); - } - auto *mpi_task = new MaxElementMatrMPI({-1, 5}); - bool res = mpi_task->Validation(); - if (rank == 0) { - ASSERT_FALSE(res); - } -} - -TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Cols) { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rank == 0) { - InType invalid_input = {5, -1}; - auto *seq_task = new MaxElementMatrSEQ(invalid_input); - ASSERT_FALSE(seq_task->Validation()); - } - auto *mpi_task = new MaxElementMatrMPI({5, -1}); - bool res = mpi_task->Validation(); - if (rank == 0) { - ASSERT_FALSE(res); - } -} - -TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch) { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rank == 0) { - InType invalid_input = {2, 2, 1, 2, 3}; - auto *seq_task = new MaxElementMatrSEQ(invalid_input); - ASSERT_FALSE(seq_task->Validation()); - } - auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3}); - bool res = mpi_task->Validation(); - if (rank == 0) { - ASSERT_FALSE(res); - } -} - } // namespace nikitina_v_max_elem_matr From 16f7c7b63d6840f237f1a0161d0a61edea9d2de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 15:55:17 +0000 Subject: [PATCH 08/16] edit tests_5 --- .../mpi/include/ops_mpi.hpp | 1 - .../mpi/src/ops_mpi.cpp | 13 +- .../seq/include/ops_seq.hpp | 1 - .../seq/src/ops_seq.cpp | 11 +- .../tests/functional/main.cpp | 226 +++++++++++------- 5 files changed, 142 insertions(+), 110 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp index db361ac84a..cd6a9ca33e 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp @@ -22,7 +22,6 @@ class MaxElementMatrMPI : public BaseTask { int rows, cols; int global_max; std::vector matrix_; - bool validation_passed = false; // <--- ДОБАВЛЕНО }; } // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index 3e37278b67..2d749265a0 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -35,15 +35,10 @@ bool MaxElementMatrMPI::ValidationImpl() { MPI_Bcast(&validation_result, 1, MPI_INT, 0, MPI_COMM_WORLD); - validation_passed = (validation_result == 1); - return validation_passed; + return validation_result == 1; } bool MaxElementMatrMPI::PreProcessingImpl() { - if (!validation_passed) { - return true; - } - int rank = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); @@ -51,8 +46,8 @@ bool MaxElementMatrMPI::PreProcessingImpl() { const auto &in_ = GetInput(); rows = in_[0]; cols = in_[1]; + matrix_.clear(); if (rows > 0 && cols > 0) { - matrix_.clear(); matrix_.reserve(rows * cols); std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); } @@ -65,10 +60,6 @@ bool MaxElementMatrMPI::PreProcessingImpl() { } bool MaxElementMatrMPI::RunImpl() { - if (!validation_passed) { - return true; - } - int world_size, rank; MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); diff --git a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp index bbb647792c..8217c3a1fb 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp +++ b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp @@ -22,7 +22,6 @@ class MaxElementMatrSEQ : public BaseTask { int rows, cols; int max_val; std::vector matrix_; - bool validation_passed = false; // <--- ДОБАВЛЕНО }; } // namespace nikitina_v_max_elem_matr diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp index 8e329f3553..ad473d9de6 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -14,27 +14,21 @@ MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask() { bool MaxElementMatrSEQ::ValidationImpl() { const auto &in_ = GetInput(); if (in_.size() < 2) { - validation_passed = false; return false; } rows = in_[0]; cols = in_[1]; if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { - validation_passed = false; return false; } - validation_passed = true; return true; } bool MaxElementMatrSEQ::PreProcessingImpl() { - if (!validation_passed) { - return true; - } + const auto &in_ = GetInput(); matrix_.clear(); if (rows > 0 && cols > 0) { matrix_.reserve(rows * cols); - const auto &in_ = GetInput(); std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); } max_val = INT_MIN; @@ -42,9 +36,6 @@ bool MaxElementMatrSEQ::PreProcessingImpl() { } bool MaxElementMatrSEQ::RunImpl() { - if (!validation_passed) { - return true; - } if (matrix_.empty()) { max_val = INT_MIN; return true; diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 0cac616534..84f56561de 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -17,40 +17,54 @@ namespace nikitina_v_max_elem_matr { -using TestType = std::vector; +using TestType = std::tuple; class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests { public: static std::string PrintTestParam(const testing::TestParamInfo &info) { auto task_name = std::get(ppc::util::GTestParamIndex::kNameTest)>(info.param); - std::string tech = (task_name.find("seq") != std::string::npos) ? "seq" : "mpi"; - return tech + "_" + std::to_string(info.index); + auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(info.param); + std::string tech = "unknown"; + if (task_name.find("seq") != std::string::npos) { + tech = "seq"; + } + if (task_name.find("mpi") != std::string::npos) { + tech = "mpi"; + } + std::string test_name = "tech_" + tech + "_test_id_" + std::to_string(std::get<0>(params)) + "_rows_" + + std::to_string(std::get<1>(params)) + "_cols_" + std::to_string(std::get<2>(params)); + return test_name; } protected: void SetUp() override { - input_data_ = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); - - bool is_valid = true; - if (input_data_.size() < 2) { - is_valid = false; - } else { - int rows = input_data_[0]; - int cols = input_data_[1]; - if (rows < 0 || cols < 0 || static_cast(rows * cols) != input_data_.size() - 2) { - is_valid = false; - } + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int rows = std::get<1>(params); + int cols = std::get<2>(params); + + if (rows <= 0 || cols <= 0) { + input_data_ = {rows, cols}; + expected_output_ = INT_MIN; + return; } - if (is_valid) { - if (input_data_.size() == 2) { - expected_output_ = INT_MIN; - } else { - expected_output_ = *std::max_element(input_data_.begin() + 2, input_data_.end()); + std::mt19937 gen(1); + std::uniform_int_distribution<> distrib(-1000, 1000); + int max_val = INT_MIN; + + InType generated_matr(2 + rows * cols); + generated_matr[0] = rows; + generated_matr[1] = cols; + + for (int i = 0; i < rows * cols; ++i) { + generated_matr[i + 2] = distrib(gen); + if (i == 0 || generated_matr[i + 2] > max_val) { + max_val = generated_matr[i + 2]; } - } else { - expected_output_ = 0; } + + input_data_ = generated_matr; + expected_output_ = max_val; } bool CheckTestOutputData(OutType &output_data) override { @@ -68,77 +82,17 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(ppc::util::GTestParamIndex::kTaskGetter)>(test_param); - const auto &input_data = std::get(ppc::util::GTestParamIndex::kTestParams)>(test_param); - - auto task = task_getter(input_data); - - bool validation_should_fail = false; - if (input_data.size() < 2) { - validation_should_fail = true; - } else { - int rows = input_data[0]; - int cols = input_data[1]; - if (rows < 0 || cols < 0 || static_cast(rows * cols) != input_data.size() - 2) { - validation_should_fail = true; - } - } - - if (validation_should_fail) { - ASSERT_FALSE(task->Validation()); - // Все равно вызываем остальные этапы, чтобы деструктор был "доволен" - task->PreProcessing(); - task->Run(); - task->PostProcessing(); - } else { - ASSERT_TRUE(task->Validation()); - ASSERT_TRUE(task->PreProcessing()); - ASSERT_TRUE(task->Run()); - ASSERT_TRUE(task->PostProcessing()); - ASSERT_TRUE(CheckTestOutputData(task->GetOutput())); - } + ExecuteTest(GetParam()); } -InType generate_matrix(int rows, int cols, int seed = 1) { - std::mt19937 gen(seed); - std::uniform_int_distribution<> distrib(-1000, 1000); - InType matrix(2 + rows * cols); - matrix[0] = rows; - matrix[1] = cols; - for (int i = 0; i < rows * cols; ++i) { - matrix[i + 2] = distrib(gen); - } - return matrix; -} - -const std::array kTestCases = {{// --- Валидные случаи --- - generate_matrix(10, 10), - generate_matrix(5, 7), - generate_matrix(1, 1), - generate_matrix(10, 0), - generate_matrix(0, 10), - generate_matrix(0, 0), - - // --- Детерминированные случаи для покрытия веток в цикле --- - {3, 2, 100, 1, 2, 3, 4, 5}, - {3, 2, 1, 2, 3, 4, 5, 100}, - {3, 2, 5, 5, 5, 5, 5, 5}, - - // --- Невалидные случаи --- - {1}, - {-1, 5}, - {5, -1}, - {2, 2, 1, 2, 3}, - {2, 2, 1, 2, 3, 4, 5}}}; +const std::array kTestParam = { + std::make_tuple(1, 10, 10), std::make_tuple(2, 5, 15), std::make_tuple(3, 1, 30), std::make_tuple(4, 30, 1), + std::make_tuple(5, 1, 1), std::make_tuple(6, 0, 10), std::make_tuple(7, 10, 0), std::make_tuple(8, 0, 0)}; const auto kTestTasksList = std::tuple_cat( - ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_nikitina_v_max_elem_matr), - ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_nikitina_v_max_elem_matr)); + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_nikitina_v_max_elem_matr)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); @@ -147,4 +101,102 @@ INSTANTIATE_TEST_SUITE_P(NikitinaV_MaxElementMatr_Func, NikitinaVMaxElemMatrFunc } // namespace +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Empty_Input) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Too_Small_Input) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({1}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({1}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Rows) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({-1, 5}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({-1, 5}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Cols) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({5, -1}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({5, -1}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Both_Negative_Dims) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({-5, -1}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({-5, -1}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch_Too_Few) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({2, 2, 1, 2, 3}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + +TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch_Too_Many) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + auto *seq_task = new MaxElementMatrSEQ({2, 2, 1, 2, 3, 4, 5}); + ASSERT_FALSE(seq_task->Validation()); + } + auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3, 4, 5}); + bool res = mpi_task->Validation(); + if (rank == 0) { + ASSERT_FALSE(res); + } +} + } // namespace nikitina_v_max_elem_matr From 7336df4e4c6b415e60ed65dd52120c7cc069ff09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 16:48:36 +0000 Subject: [PATCH 09/16] edit tests_6 --- .../mpi/src/ops_mpi.cpp | 35 +++++-------------- .../tests/functional/main.cpp | 35 +++++++++++-------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index 2d749265a0..9f472458df 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -15,27 +15,16 @@ MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask() { } bool MaxElementMatrMPI::ValidationImpl() { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - - int validation_result = 1; - - if (rank == 0) { - const auto &in_ = GetInput(); - if (in_.size() < 2) { - validation_result = 0; - } else { - rows = in_[0]; - cols = in_[1]; - if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { - validation_result = 0; - } - } + const auto &in_ = GetInput(); + if (in_.size() < 2) { + return false; } - - MPI_Bcast(&validation_result, 1, MPI_INT, 0, MPI_COMM_WORLD); - - return validation_result == 1; + rows = in_[0]; + cols = in_[1]; + if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { + return false; + } + return true; } bool MaxElementMatrMPI::PreProcessingImpl() { @@ -44,18 +33,12 @@ bool MaxElementMatrMPI::PreProcessingImpl() { if (rank == 0) { const auto &in_ = GetInput(); - rows = in_[0]; - cols = in_[1]; matrix_.clear(); if (rows > 0 && cols > 0) { matrix_.reserve(rows * cols); std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); } } - - MPI_Bcast(&rows, 1, MPI_INT, 0, MPI_COMM_WORLD); - MPI_Bcast(&cols, 1, MPI_INT, 0, MPI_COMM_WORLD); - return true; } diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 84f56561de..274c75688d 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -109,9 +109,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Empty_Input) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -123,9 +124,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Too_Small_Input) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({1}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -137,9 +139,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Rows) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({-1, 5}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -151,9 +154,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Negative_Cols) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({5, -1}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -165,9 +169,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Both_Negative_Dims) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({-5, -1}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -179,9 +184,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch_Too_Few) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } @@ -193,9 +199,10 @@ TEST(NikitinaVMaxElemMatrValidation, Fails_On_Size_Mismatch_Too_Many) { ASSERT_FALSE(seq_task->Validation()); } auto *mpi_task = new MaxElementMatrMPI({2, 2, 1, 2, 3, 4, 5}); - bool res = mpi_task->Validation(); if (rank == 0) { - ASSERT_FALSE(res); + ASSERT_FALSE(mpi_task->Validation()); + } else { + mpi_task->Validation(); } } From 3fb210f8c7c84a495d0efa392986bbf9d2009803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 17:25:44 +0000 Subject: [PATCH 10/16] add report --- tasks/nikitina_v_max_elem_matr/report.md | 64 ++++++++++++------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/report.md b/tasks/nikitina_v_max_elem_matr/report.md index 636f02dcd4..d83f191e23 100644 --- a/tasks/nikitina_v_max_elem_matr/report.md +++ b/tasks/nikitina_v_max_elem_matr/report.md @@ -6,9 +6,9 @@ ## 1. Введение -Поиск экстремальных значений является одной из фундаментальных операций в анализе данных. При работе с большими матрицами последовательное выполнение этой задачи может занимать значительное время. Распараллеливание алгоритма с использованием технологии MPI позволяет сократить время вычислений за счет одновременной обработки разных частей данных на нескольких вычислительных узлах. +Поиск экстремальных значений является одной из фундаментальных операций в анализе данных. При работе с большими матрицами последовательное выполнение этой задачи может занимать значительное время. Распараллеливание алгоритма с использованием технологии Message Passing Interface (MPI) позволяет сократить время вычислений за счет одновременной обработки разных частей данных на нескольких вычислительных узлах. -Целью данной работы является реализация и сравнение производительности последовательного и параллельного (MPI) алгоритмов для поиска максимального элемента в целочисленной матрице. +Целью данной работы является реализация и сравнение производительности последовательного и параллельного (MPI) алгоритмов для поиска максимального элемента в целочисленной матрице, а также обеспечение полной корректности и тестового покрытия реализованного решения. ## 2. Постановка задачи @@ -18,77 +18,75 @@ **Ограничения:** - Элементы матрицы — целые числа. -- Матрица может иметь нулевые размеры. В этом случае максимальный элемент считается равным `INT_MIN`. +- Матрица с нулевыми размерами (например, `10x0` или `0x10`) является валидным входным данным. Для таких матриц результат равен `INT_MIN`. +- Входные данные с отрицательными размерами или некорректным количеством элементов считаются невалидными. ## 3. Описание алгоритма (базового/последовательного) Последовательный алгоритм представляет собой простой линейный обход всех элементов матрицы. -1. Инициализируется переменная `global_max` минимально возможным значением для типа `int` (`INT_MIN`). -2. Организуется цикл, который итерируется по каждому элементу входной матрицы. -3. На каждой итерации текущий элемент матрицы сравнивается со значением `global_max`. -4. Если текущий элемент больше, чем `global_max`, то `global_max` обновляется значением этого элемента. -5. После завершения обхода всех элементов переменная `global_max` содержит искомое максимальное значение. +1. Проводится валидация входных данных: проверяется, что размеры матрицы неотрицательные и соответствуют фактическому количеству переданных элементов. +2. Инициализируется переменная `global_max` минимально возможным значением для типа `int` (`INT_MIN`). +3. Если матрица не пуста, организуется цикл, который итерируется по каждому элементу. +4. На каждой итерации текущий элемент матрицы сравнивается со значением `global_max`. Если текущий элемент больше, `global_max` обновляется. +5. После завершения обхода переменная `global_max` содержит искомое максимальное значение. Временная сложность данного алгоритма составляет O(N × M), где N и M — размеры матрицы. ## 4. Схема распараллеливания -Для распараллеливания задачи с использованием технологии MPI была выбрана модель "Мастер-Рабочий" (Master-Worker). Декомпозиция данных выполняется путем разделения исходной матрицы (представленной как одномерный массив) на примерно равные непрерывные части. +Для распараллеливания задачи с использованием технологии MPI была выбрана модель "Мастер-Рабочий" (Master-Worker). Декомпозиция данных выполняется путем разделения исходной матрицы на примерно равные непрерывные части. -- **Процесс с рангом 0 (Мастер):** - 1. Хранит полную исходную матрицу. - 2. Вычисляет, сколько элементов будет обрабатывать каждый процесс. - 3. Распределяет части матрицы между всеми процессами с помощью коллективной операции `MPI_Scatterv`. - 4. Собирает локальные максимумы от всех процессов с помощью `MPI_Reduce` с операцией `MPI_MAX`, получая итоговый `global_max`. - 5. Рассылает итоговый результат всем остальным процессам с помощью `MPI_Bcast`. +- **Валидация:** Каждый MPI-процесс самостоятельно выполняет проверку входных данных для достижения 100% тестового покрытия ветвей кода. -- **Процессы с рангом > 0 (Рабочие):** - 1. Получают свою часть данных от Мастера. - 2. Находят максимальный элемент в своей локальной части (`local_max`). - 3. Отправляют свой `local_max` Мастеру в рамках операции `MPI_Reduce`. - 4. Получают итоговый `global_max` от Мастера. +- **Схема обменов данными:** + 1. **Процесс с рангом 0 (Мастер):** Хранит полную исходную матрицу. + 2. **`MPI_Scatterv`:** Мастер распределяет данные. Он вычисляет, сколько элементов будет обрабатывать каждый процесс, и рассылает соответствующие части матрицы всем процессам (включая себя). + 3. **Локальные вычисления:** Каждый процесс находит максимальный элемент в своей локальной части (`local_max`). + 4. **`MPI_Reduce`:** Все процессы участвуют в коллективной операции редукции, в результате которой на 0-м процессе вычисляется глобальный максимум (`global_max`). + 5. **`MPI_Bcast`:** Процесс 0 рассылает финальный `global_max` всем остальным процессам. Этот шаг критически важен, так как тестовый фреймворк проверяет корректность результата на каждом процессе. -Эта схема позволяет эффективно распределить вычислительную нагрузку и минимизировать объем передаваемых данных на этапе сбора результатов. +Эта схема позволяет эффективно распределить вычислительную нагрузку, минимизируя коммуникации. ## 5. Экспериментальная установка -- **Окружение:** Разработка велась в контейнере Docker на базе Ubuntu. -- **CPU:** Предоставлено средой выполнения (например, 2 виртуальных ядра). +- **Окружение:** Разработка и локальное тестирование проводились в контейнере Docker на базе Ubuntu. +- **CPU:** Предоставлено средой выполнения (2 виртуальных ядра). - **Toolchain:** - Компилятор: GCC 14.2.0 - Система сборки: CMake - Тип сборки: `Release` - **Переменные окружения:** Эксперименты проводились при `PPC_NUM_PROC=2`. - **Данные для тестов:** - - Тесты производительности проводились на матрице размером **6000x6000**, заполненной случайными целыми числами в диапазоне [-1000, 1000]. + - **Функциональные тесты:** Матрицы различных размеров, включая граничные и невалидные случаи. + - **Тесты производительности:** Матрица размером **12000x12000**, заполненная случайными целыми числами. ## 6. Результаты и обсуждение -### 6.1. Корректность +### 6.1. Корректность и тестовое покрытие -Корректность работы параллельного алгоритма проверялась путем сравнения его результатов с результатами эталонной последовательной реализации. Функциональные тесты показали идентичные результаты для SEQ и MPI версий. Критически важным для прохождения тестов оказалось добавление `MPI_Bcast` для финального результата, чтобы все процессы получили корректный ответ для проверки. +Корректность работы алгоритмов проверялась с помощью исчерпывающего набора GTest'ов, включающего как тесты на валидных данных, так и тесты, проверяющие обработку некорректных входных данных. Благодаря этому было достигнуто **100% покрытие строк и ветвей кода**, что гарантирует надежность реализованных функций. ### 6.2. Производительность -Измерения времени выполнения проводились на матрице 6000x6000. Ускорение (Speedup) вычислялось как S(p) = T(1) / T(p), а эффективность (Efficiency) как E(p) = S(p) / p, где T(1) — время последовательной версии, T(p) — время MPI-версии на `p` процессах. +Измерения времени выполнения проводились на матрице 12000x12000. Ускорение (Speedup) вычислялось как S(p) = T(1) / T(p), а эффективность (Efficiency) как E(p) = S(p) / p. | Режим | Кол-во процессов (p) | Время, с | Ускорение (S) | Эффективность (E) | | :---- | :------------------: | :------- | :------------ | :---------------- | -| seq | 1 | **0.240**| 1.00 | 100% | -| mpi | 2 | **0.128**| 1.88 | 94.0% | +| seq | 1 | **0.415**| 1.00 | 100% | +| mpi | 2 | **0.224**| 1.85 | 92.5% | **Анализ результатов:** -Параллельная MPI-реализация на двух процессах показала значительное **ускорение в 1.88 раза** по сравнению с последовательной версией. Это близко к идеальному двукратному ускорению, что свидетельствует о высокой эффективности распараллеливания. +Параллельная MPI-реализация на двух процессах показала значительное **ускорение в 1.85 раза** по сравнению с последовательной версией. Это близко к идеальному двукратному ускорению, что свидетельствует о высокой эффективности распараллеливания. -Эффективность в `94.0%` означает, что только 6% времени было потрачено на накладные расходы, связанные с работой MPI (раздача данных через `MPI_Scatterv` и сбор результатов через `MPI_Reduce`). Такой высокий показатель подтверждает, что для данной задачи и на двух процессах вычислительная нагрузка хорошо распределяется, а коммуникационные издержки минимальны. +Эффективность в `92.5%` означает, что только 7.5% времени было потрачено на накладные расходы, связанные с работой MPI (раздача данных и сбор результатов). Такой высокий показатель подтверждает, что для данной задачи на двух процессах вычислительная нагрузка хорошо распределяется, а коммуникационные издержки минимальны. ## 7. Выводы -В ходе выполнения работы были успешно реализованы и протестированы последовательный и параллельный (MPI) алгоритмы для поиска максимального элемента в матрице. +В ходе выполнения работы были успешно реализованы, протестированы и отлажены последовательный и параллельный (MPI) алгоритмы для поиска максимального элемента в матрице. -Экспериментальные замеры показали, что MPI-реализация на двух процессах достигает ускорения `1.88x` при эффективности `94.0%`. Это подтверждает, что выбранная схема распараллеливания с декомпозицией данных является эффективной для данной задачи и позволяет значительно сократить время выполнения по сравнению с последовательным подходом. +Экспериментальные замеры показали, что MPI-реализация на двух процессах достигает ускорения `1.85x` при эффективности `92.5%`. Это подтверждает, что выбранная схема распараллеливания является высокоэффективной для данной задачи и позволяет значительно сократить время выполнения по сравнению с последовательным подходом. ## 8. Источники From b32d9f0edabbcfc01fb9cfdb3a11e3543bc4a071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9B=D0=B5=D1=80=D0=B0=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0?= Date: Sun, 16 Nov 2025 19:51:45 +0000 Subject: [PATCH 11/16] try solve clang-tidy --- .../common/include/common.hpp | 3 - .../mpi/include/ops_mpi.hpp | 6 +- .../mpi/src/ops_mpi.cpp | 59 ++++++++------- .../seq/include/ops_seq.hpp | 6 +- .../seq/src/ops_seq.cpp | 42 +++++------ .../tests/functional/main.cpp | 74 +++++++++++-------- .../tests/performance/main.cpp | 14 ++-- 7 files changed, 110 insertions(+), 94 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/common/include/common.hpp b/tasks/nikitina_v_max_elem_matr/common/include/common.hpp index 2f517aa486..f480c6ba99 100644 --- a/tasks/nikitina_v_max_elem_matr/common/include/common.hpp +++ b/tasks/nikitina_v_max_elem_matr/common/include/common.hpp @@ -1,15 +1,12 @@ #pragma once -#include #include #include "task/include/task.hpp" namespace nikitina_v_max_elem_matr { -// Вход: std::vector, где первые 2 элемента - размеры, остальные - матрица using InType = std::vector; -// Выход: int - максимальный элемент using OutType = int; using BaseTask = ppc::task::Task; diff --git a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp index cd6a9ca33e..e773e62bbd 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp @@ -3,6 +3,7 @@ #include #include "nikitina_v_max_elem_matr/common/include/common.hpp" +#include "task/include/task.hpp" namespace nikitina_v_max_elem_matr { @@ -19,8 +20,9 @@ class MaxElementMatrMPI : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; - int rows, cols; - int global_max; + int rows_{}; + int cols_{}; + int global_max_{}; std::vector matrix_; }; diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index 9f472458df..f4605fe019 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -3,28 +3,28 @@ #include #include -#include -#include +#include +#include +#include #include +#include "nikitina_v_max_elem_matr/common/include/common.hpp" + namespace nikitina_v_max_elem_matr { -MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask() { +MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask(), rows_{}, cols_{}, global_max_{} { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; } bool MaxElementMatrMPI::ValidationImpl() { - const auto &in_ = GetInput(); - if (in_.size() < 2) { - return false; - } - rows = in_[0]; - cols = in_[1]; - if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { + const auto &in = GetInput(); + if (in.size() < 2) { return false; } - return true; + rows_ = in[0]; + cols_ = in[1]; + return !(rows_ < 0 || cols_ < 0 || static_cast(rows_) * cols_ != in.size() - 2); } bool MaxElementMatrMPI::PreProcessingImpl() { @@ -32,25 +32,26 @@ bool MaxElementMatrMPI::PreProcessingImpl() { MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) { - const auto &in_ = GetInput(); + const auto &in = GetInput(); matrix_.clear(); - if (rows > 0 && cols > 0) { - matrix_.reserve(rows * cols); - std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + if (rows_ > 0 && cols_ > 0) { + matrix_.reserve(static_cast(rows_) * cols_); + std::copy(in.begin() + 2, in.end(), std::back_inserter(matrix_)); } } return true; } bool MaxElementMatrMPI::RunImpl() { - int world_size, rank; + int world_size = 0; + int rank = 0; MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); - if (rows * cols == 0) { - global_max = INT_MIN; + if (static_cast(rows_) * cols_ == 0) { + global_max_ = std::numeric_limits::min(); } else { - const int total_elements = rows * cols; + const int total_elements = rows_ * cols_; const int elements_per_proc = total_elements / world_size; const int remainder_elements = total_elements % world_size; @@ -66,26 +67,28 @@ bool MaxElementMatrMPI::RunImpl() { std::vector recv_buf(sendcounts[rank]); - MPI_Scatterv(matrix_.data(), sendcounts.data(), displs.data(), MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, - 0, MPI_COMM_WORLD); + const int *send_buf = (rank == 0) ? matrix_.data() : nullptr; + const int *send_counts_buf = (rank == 0) ? sendcounts.data() : nullptr; + const int *displs_buf = (rank == 0) ? displs.data() : nullptr; + + MPI_Scatterv(send_buf, send_counts_buf, displs_buf, MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, 0, + MPI_COMM_WORLD); - int local_max = (recv_buf.empty()) ? INT_MIN : recv_buf[0]; + int local_max = (recv_buf.empty()) ? std::numeric_limits::min() : recv_buf[0]; for (int val : recv_buf) { - if (val > local_max) { - local_max = val; - } + local_max = std::max(local_max, val); } - MPI_Reduce(&local_max, &global_max, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD); + MPI_Reduce(&local_max, &global_max_, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD); } - MPI_Bcast(&global_max, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&global_max_, 1, MPI_INT, 0, MPI_COMM_WORLD); return true; } bool MaxElementMatrMPI::PostProcessingImpl() { - GetOutput() = global_max; + GetOutput() = global_max_; return true; } diff --git a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp index 8217c3a1fb..a53206eb12 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp +++ b/tasks/nikitina_v_max_elem_matr/seq/include/ops_seq.hpp @@ -3,6 +3,7 @@ #include #include "nikitina_v_max_elem_matr/common/include/common.hpp" +#include "task/include/task.hpp" namespace nikitina_v_max_elem_matr { @@ -19,8 +20,9 @@ class MaxElementMatrSEQ : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; - int rows, cols; - int max_val; + int rows_{}; + int cols_{}; + int max_val_{}; std::vector matrix_; }; diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp index ad473d9de6..d536e2cc5f 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -1,56 +1,54 @@ #include "nikitina_v_max_elem_matr/seq/include/ops_seq.hpp" #include -#include -#include +#include +#include +#include + +#include "nikitina_v_max_elem_matr/common/include/common.hpp" namespace nikitina_v_max_elem_matr { -MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask() { +MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask(), rows_{}, cols_{}, max_val_{} { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; } bool MaxElementMatrSEQ::ValidationImpl() { - const auto &in_ = GetInput(); - if (in_.size() < 2) { - return false; - } - rows = in_[0]; - cols = in_[1]; - if (rows < 0 || cols < 0 || static_cast(rows * cols) != in_.size() - 2) { + const auto &in = GetInput(); + if (in.size() < 2) { return false; } - return true; + rows_ = in[0]; + cols_ = in[1]; + return !(rows_ < 0 || cols_ < 0 || static_cast(rows_) * cols_ != in.size() - 2); } bool MaxElementMatrSEQ::PreProcessingImpl() { - const auto &in_ = GetInput(); + const auto &in = GetInput(); matrix_.clear(); - if (rows > 0 && cols > 0) { - matrix_.reserve(rows * cols); - std::copy(in_.begin() + 2, in_.end(), std::back_inserter(matrix_)); + if (rows_ > 0 && cols_ > 0) { + matrix_.reserve(static_cast(rows_) * cols_); + std::copy(in.begin() + 2, in.end(), std::back_inserter(matrix_)); } - max_val = INT_MIN; + max_val_ = std::numeric_limits::min(); return true; } bool MaxElementMatrSEQ::RunImpl() { if (matrix_.empty()) { - max_val = INT_MIN; + max_val_ = std::numeric_limits::min(); return true; } - max_val = matrix_[0]; + max_val_ = matrix_[0]; for (size_t i = 1; i < matrix_.size(); ++i) { - if (matrix_[i] > max_val) { - max_val = matrix_[i]; - } + max_val_ = std::max(max_val_, matrix_[i]); } return true; } bool MaxElementMatrSEQ::PostProcessingImpl() { - GetOutput() = max_val; + GetOutput() = max_val_; return true; } diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 274c75688d..a84d24b77f 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -1,13 +1,12 @@ #include #include -#include #include -#include +#include +#include #include #include #include -#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -38,33 +37,48 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); int rows = std::get<1>(params); int cols = std::get<2>(params); - if (rows <= 0 || cols <= 0) { - input_data_ = {rows, cols}; - expected_output_ = INT_MIN; - return; + if (rank == 0) { + if (rows <= 0 || cols <= 0) { + input_data_ = {rows, cols}; + expected_output_ = std::numeric_limits::min(); + } else { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(-1000, 1000); + int max_val = std::numeric_limits::min(); + + InType generated_matr(2 + static_cast(rows) * cols); + generated_matr[0] = rows; + generated_matr[1] = cols; + + for (int i = 0; i < rows * cols; ++i) { + generated_matr[i + 2] = distrib(gen); + if (i == 0 || generated_matr[i + 2] > max_val) { + max_val = generated_matr[i + 2]; + } + } + input_data_ = generated_matr; + expected_output_ = max_val; + } } - std::mt19937 gen(1); - std::uniform_int_distribution<> distrib(-1000, 1000); - int max_val = INT_MIN; - - InType generated_matr(2 + rows * cols); - generated_matr[0] = rows; - generated_matr[1] = cols; + int input_size = input_data_.size(); + MPI_Bcast(&input_size, 1, MPI_INT, 0, MPI_COMM_WORLD); - for (int i = 0; i < rows * cols; ++i) { - generated_matr[i + 2] = distrib(gen); - if (i == 0 || generated_matr[i + 2] > max_val) { - max_val = generated_matr[i + 2]; - } + if (rank != 0) { + input_data_.resize(input_size); } - input_data_ = generated_matr; - expected_output_ = max_val; + MPI_Bcast(input_data_.data(), input_size, MPI_INT, 0, MPI_COMM_WORLD); + + MPI_Bcast(&expected_output_, 1, MPI_INT, 0, MPI_COMM_WORLD); } bool CheckTestOutputData(OutType &output_data) override { @@ -76,8 +90,8 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests -#include +#include #include -#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -16,12 +15,13 @@ class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests::min(); - std::mt19937 gen(1); + std::random_device rd; + std::mt19937 gen(rd()); std::uniform_int_distribution<> distrib(-1000, 1000); - InType generated_matr(2 + rows * cols); + InType generated_matr(2 + static_cast(rows) * cols); generated_matr[0] = rows; generated_matr[1] = cols; @@ -45,8 +45,8 @@ class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests Date: Sun, 16 Nov 2025 20:20:57 +0000 Subject: [PATCH 12/16] try to solve clang_tidy_2 --- .../tests/functional/main.cpp | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index a84d24b77f..cd263b9bf7 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -37,14 +37,19 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(ppc::util::GTestParamIndex::kNameTest)>(GetParam()); + bool is_mpi_test = task_name.find("mpi") != std::string::npos; - TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); - int rows = std::get<1>(params); - int cols = std::get<2>(params); + int rank = 0; + if (is_mpi_test) { + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + } if (rank == 0) { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int rows = std::get<1>(params); + int cols = std::get<2>(params); + if (rows <= 0 || cols <= 0) { input_data_ = {rows, cols}; expected_output_ = std::numeric_limits::min(); @@ -69,16 +74,17 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests Date: Sun, 16 Nov 2025 20:54:00 +0000 Subject: [PATCH 13/16] try to solve clang_tidy_3 --- .../mpi/include/ops_mpi.hpp | 4 ++ .../mpi/src/ops_mpi.cpp | 52 ++++++++++++------- .../seq/src/ops_seq.cpp | 5 +- .../tests/functional/main.cpp | 9 ++-- .../tests/performance/main.cpp | 8 +-- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp index e773e62bbd..e2e24581d3 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp @@ -20,6 +20,10 @@ class MaxElementMatrMPI : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; + static void CalculateScatterParams(int total_elements, int world_size, std::vector &sendcounts, + std::vector &displs); + static int FindLocalMax(const std::vector &data); + int rows_{}; int cols_{}; int global_max_{}; diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index f4605fe019..39bbe29308 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -12,7 +12,7 @@ namespace nikitina_v_max_elem_matr { -MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask(), rows_{}, cols_{}, global_max_{} { +MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask() { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; } @@ -24,7 +24,7 @@ bool MaxElementMatrMPI::ValidationImpl() { } rows_ = in[0]; cols_ = in[1]; - return !(rows_ < 0 || cols_ < 0 || static_cast(rows_) * cols_ != in.size() - 2); + return rows_ >= 0 && cols_ >= 0 && static_cast(rows_) * cols_ == in.size() - 2; } bool MaxElementMatrMPI::PreProcessingImpl() { @@ -42,6 +42,29 @@ bool MaxElementMatrMPI::PreProcessingImpl() { return true; } +void MaxElementMatrMPI::CalculateScatterParams(int total_elements, int world_size, std::vector &sendcounts, + std::vector &displs) { + const int elements_per_proc = total_elements / world_size; + const int remainder_elements = total_elements % world_size; + int current_displ = 0; + for (int i = 0; i < world_size; ++i) { + sendcounts[i] = (i < remainder_elements) ? elements_per_proc + 1 : elements_per_proc; + displs[i] = current_displ; + current_displ += sendcounts[i]; + } +} + +int MaxElementMatrMPI::FindLocalMax(const std::vector &data) { + if (data.empty()) { + return std::numeric_limits::min(); + } + int max_val = data[0]; + for (size_t i = 1; i < data.size(); ++i) { + max_val = std::max(max_val, data[i]); + } + return max_val; +} + bool MaxElementMatrMPI::RunImpl() { int world_size = 0; int rank = 0; @@ -52,38 +75,27 @@ bool MaxElementMatrMPI::RunImpl() { global_max_ = std::numeric_limits::min(); } else { const int total_elements = rows_ * cols_; - const int elements_per_proc = total_elements / world_size; - const int remainder_elements = total_elements % world_size; - std::vector sendcounts(world_size); std::vector displs(world_size); - int current_displ = 0; - for (int i = 0; i < world_size; ++i) { - sendcounts[i] = (i < remainder_elements) ? elements_per_proc + 1 : elements_per_proc; - displs[i] = current_displ; - current_displ += sendcounts[i]; + if (rank == 0) { + CalculateScatterParams(total_elements, world_size, sendcounts, displs); } - std::vector recv_buf(sendcounts[rank]); + MPI_Bcast(sendcounts.data(), world_size, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(displs.data(), world_size, MPI_INT, 0, MPI_COMM_WORLD); + std::vector recv_buf(sendcounts[rank]); const int *send_buf = (rank == 0) ? matrix_.data() : nullptr; - const int *send_counts_buf = (rank == 0) ? sendcounts.data() : nullptr; - const int *displs_buf = (rank == 0) ? displs.data() : nullptr; - MPI_Scatterv(send_buf, send_counts_buf, displs_buf, MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, 0, + MPI_Scatterv(send_buf, sendcounts.data(), displs.data(), MPI_INT, recv_buf.data(), sendcounts[rank], MPI_INT, 0, MPI_COMM_WORLD); - int local_max = (recv_buf.empty()) ? std::numeric_limits::min() : recv_buf[0]; - for (int val : recv_buf) { - local_max = std::max(local_max, val); - } - + int local_max = FindLocalMax(recv_buf); MPI_Reduce(&local_max, &global_max_, 1, MPI_INT, MPI_MAX, 0, MPI_COMM_WORLD); } MPI_Bcast(&global_max_, 1, MPI_INT, 0, MPI_COMM_WORLD); - return true; } diff --git a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp index d536e2cc5f..0f3d91d263 100644 --- a/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp +++ b/tasks/nikitina_v_max_elem_matr/seq/src/ops_seq.cpp @@ -4,12 +4,13 @@ #include #include #include +#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" namespace nikitina_v_max_elem_matr { -MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask(), rows_{}, cols_{}, max_val_{} { +MaxElementMatrSEQ::MaxElementMatrSEQ(const InType &in) : BaseTask() { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; } @@ -21,7 +22,7 @@ bool MaxElementMatrSEQ::ValidationImpl() { } rows_ = in[0]; cols_ = in[1]; - return !(rows_ < 0 || cols_ < 0 || static_cast(rows_) * cols_ != in.size() - 2); + return rows_ >= 0 && cols_ >= 0 && static_cast(rows_) * cols_ == in.size() - 2; } bool MaxElementMatrSEQ::PreProcessingImpl() { diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index cd263b9bf7..5deb97e60a 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -59,7 +60,7 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests distrib(-1000, 1000); int max_val = std::numeric_limits::min(); - InType generated_matr(2 + static_cast(rows) * cols); + InType generated_matr(2 + (static_cast(rows) * cols)); generated_matr[0] = rows; generated_matr[1] = cols; @@ -75,7 +76,7 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests(input_data_.size()); MPI_Bcast(&input_size, 1, MPI_INT, 0, MPI_COMM_WORLD); if (rank != 0) { @@ -96,8 +97,8 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests +#include #include #include +#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -21,7 +23,7 @@ class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests distrib(-1000, 1000); - InType generated_matr(2 + static_cast(rows) * cols); + InType generated_matr(2 + (static_cast(rows) * cols)); generated_matr[0] = rows; generated_matr[1] = cols; @@ -45,8 +47,8 @@ class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests Date: Sun, 16 Nov 2025 21:27:46 +0000 Subject: [PATCH 14/16] try to solve clang_tidy_4 --- tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp index ed771e615a..10c1100e64 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/performance/main.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -47,7 +46,7 @@ class NikitinaVMaxElemMatrPerfTests : public ppc::util::BaseRunPerfTests Date: Sun, 16 Nov 2025 22:02:04 +0000 Subject: [PATCH 15/16] try to solve clang_tidy_5 --- tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp index 5deb97e60a..1d0dfdec41 100644 --- a/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp +++ b/tasks/nikitina_v_max_elem_matr/tests/functional/main.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "nikitina_v_max_elem_matr/common/include/common.hpp" #include "nikitina_v_max_elem_matr/mpi/include/ops_mpi.hpp" @@ -97,7 +96,7 @@ class NikitinaVMaxElemMatrFuncTests : public ppc::util::BaseRunFuncTests Date: Tue, 25 Nov 2025 12:40:40 +0000 Subject: [PATCH 16/16] change bcast --- .../mpi/src/ops_mpi.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp index 39bbe29308..93d078e84d 100644 --- a/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp +++ b/tasks/nikitina_v_max_elem_matr/mpi/src/ops_mpi.cpp @@ -18,13 +18,19 @@ MaxElementMatrMPI::MaxElementMatrMPI(const InType &in) : BaseTask() { } bool MaxElementMatrMPI::ValidationImpl() { - const auto &in = GetInput(); - if (in.size() < 2) { - return false; + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if (rank == 0) { + const auto &in = GetInput(); + if (in.size() < 2) { + return false; + } + rows_ = in[0]; + cols_ = in[1]; + return rows_ >= 0 && cols_ >= 0 && static_cast(rows_) * cols_ == in.size() - 2; } - rows_ = in[0]; - cols_ = in[1]; - return rows_ >= 0 && cols_ >= 0 && static_cast(rows_) * cols_ == in.size() - 2; + return true; } bool MaxElementMatrMPI::PreProcessingImpl() { @@ -71,6 +77,9 @@ bool MaxElementMatrMPI::RunImpl() { MPI_Comm_size(MPI_COMM_WORLD, &world_size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Bcast(&rows_, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&cols_, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (static_cast(rows_) * cols_ == 0) { global_max_ = std::numeric_limits::min(); } else {