diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp b/tasks/makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp new file mode 100644 index 0000000000..d59a02221a --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +using InType = std::vector>; +using OutType = std::vector; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/data/10000.png b/tasks/makovskiy_i_min_value_in_matrix_rows/data/10000.png new file mode 100644 index 0000000000..436635b124 Binary files /dev/null and b/tasks/makovskiy_i_min_value_in_matrix_rows/data/10000.png differ diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/info.json b/tasks/makovskiy_i_min_value_in_matrix_rows/info.json new file mode 100644 index 0000000000..3e47d7bc2f --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Илья", + "last_name": "Маковский", + "middle_name": "Игоревич", + "group_number": "3823Б1ФИ2", + "task_number": "1" + } +} diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..956cd90e0b --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +class MinValueMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit MinValueMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + void ProcessRankZero(std::vector &local_min_values); + void GatherResults(const std::vector &local_min_values); + + static void ProcessWorkerRank(std::vector &local_min_values); +}; + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/src/ops_mpi.cpp b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..058999e192 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/src/ops_mpi.cpp @@ -0,0 +1,148 @@ +#include "makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp" + +#include + +#include +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +namespace { +void SendDataToWorkers(const InType &matrix, int size, int rows_per_proc, int remaining_rows, int ¤t_row_idx) { + for (int i = 1; i < size; ++i) { + const int rows_for_this_proc = rows_per_proc + (i < remaining_rows ? 1 : 0); + MPI_Send(&rows_for_this_proc, 1, MPI_INT, i, 0, MPI_COMM_WORLD); + for (int j = 0; j < rows_for_this_proc; ++j) { + const auto &row = matrix[current_row_idx++]; + const auto row_size = static_cast(row.size()); + MPI_Send(&row_size, 1, MPI_INT, i, 1, MPI_COMM_WORLD); + if (row_size > 0) { + MPI_Send(row.data(), row_size, MPI_INT, i, 2, MPI_COMM_WORLD); + } + } + } +} +} // namespace + +void MinValueMPI::ProcessRankZero(std::vector &local_min_values) { + const auto &matrix = this->GetInput(); + int size = 0; + MPI_Comm_size(MPI_COMM_WORLD, &size); + + if (size == 0) { + return; + } + + const auto num_rows = static_cast(matrix.size()); + const int rows_per_proc = num_rows / size; + const int remaining_rows = num_rows % size; + int current_row_idx = 0; + + const int rows_for_root = rows_per_proc + (0 < remaining_rows ? 1 : 0); + for (int j = 0; j < rows_for_root; ++j) { + const auto &row = matrix[current_row_idx++]; + if (!row.empty()) { + local_min_values.push_back(*std::ranges::min_element(row)); + } + } + + if (size > 1) { + SendDataToWorkers(matrix, size, rows_per_proc, remaining_rows, current_row_idx); + } +} + +void MinValueMPI::ProcessWorkerRank(std::vector &local_min_values) { + int num_local_rows = 0; + MPI_Recv(&num_local_rows, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + for (int i = 0; i < num_local_rows; ++i) { + int row_size = 0; + MPI_Recv(&row_size, 1, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + if (row_size > 0) { + std::vector received_row(row_size); + MPI_Recv(received_row.data(), row_size, MPI_INT, 0, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + local_min_values.push_back(*std::ranges::min_element(received_row)); + } + } +} + +void MinValueMPI::GatherResults(const std::vector &local_min_values) { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const auto local_results_count = static_cast(local_min_values.size()); + std::vector recvcounts; + if (rank == 0) { + recvcounts.resize(size); + } + + MPI_Gather(&local_results_count, 1, MPI_INT, (rank == 0 ? recvcounts.data() : nullptr), 1, MPI_INT, 0, + MPI_COMM_WORLD); + + std::vector displs; + if (rank == 0 && size > 0) { + displs.resize(size, 0); + for (int i = 1; i < size; ++i) { + displs[i] = displs[i - 1] + recvcounts[i - 1]; + } + } + + MPI_Gatherv(local_min_values.data(), local_results_count, MPI_INT, (rank == 0 ? this->GetOutput().data() : nullptr), + (rank == 0 ? recvcounts.data() : nullptr), (rank == 0 ? displs.data() : nullptr), MPI_INT, 0, + MPI_COMM_WORLD); +} + +MinValueMPI::MinValueMPI(const InType &in) { + InType temp(in); + this->GetInput().swap(temp); + SetTypeOfTask(GetStaticTypeOfTask()); +} + +bool MinValueMPI::ValidationImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + int is_valid = 0; + if (rank == 0) { + const auto &mat = this->GetInput(); + is_valid = !mat.empty() ? 1 : 0; + } + MPI_Bcast(&is_valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + return is_valid == 1; +} + +bool MinValueMPI::PreProcessingImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + const auto &mat = this->GetInput(); + this->GetOutput().clear(); + this->GetOutput().resize(mat.size()); + } + return true; +} + +bool MinValueMPI::RunImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + std::vector local_min_values; + if (rank == 0) { + ProcessRankZero(local_min_values); + } else { + ProcessWorkerRank(local_min_values); + } + + GatherResults(local_min_values); + + return true; +} + +bool MinValueMPI::PostProcessingImpl() { + return true; +} + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/report.md b/tasks/makovskiy_i_min_value_in_matrix_rows/report.md new file mode 100644 index 0000000000..45cf835c08 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/report.md @@ -0,0 +1,145 @@ +# Поиск минимального значения в каждой строке матрицы + +- Студент: Маковский Илья Игоревич, группа 3823Б1ФИ2 +- Технология: SEQ, MPI +- Вариант: 17 + +## 1. Введение + +Обработка больших объемов данных, представленных в виде матриц, является фундаментальной задачей во многих областях, включая научные вычисления, машинное обучение и обработку изображений. Одной из базовых операций является агрегация данных, например, поиск минимального или максимального значения. При работе с матрицами большого размера последовательное выполнение таких операций может занимать значительное время. + +Целью данной работы является разработка и анализ параллельной реализации алгоритма поиска минимального элемента в каждой строке матрицы с использованием технологии MPI (Message Passing Interface), а также сравнение её производительности с последовательной версией на задачах разного масштаба. + +## 2. Постановка задачи + +**Дано:** матрица целых чисел размера M x N. + +**Требуется:** получить вектор L длиной M, в котором каждый элемент `L[i]` является минимальным значением в `i`-й строке исходной матрицы. + +**Входные данные:** `std::vector>` — двумерный вектор целых чисел. +**Выходные данные:** `std::vector` — вектор минимальных значений. + +**Ограничения:** Матрица и её строки могут быть непустыми. Реализация должна корректно обрабатывать случаи, когда количество строк не делится нацело на количество используемых процессов. + +## 3. Базовый алгоритм (Последовательный) + +Последовательный алгоритм является интуитивно понятным и реализуется с помощью итерации по строкам. + +1. Создаётся выходной вектор `result` размером M (по количеству строк во входной матрице). +2. Выполняется итерация по каждой строке матрицы от `i = 0` до `M-1`. +3. Для каждой `i`-й строки находится минимальный элемент. В данной реализации для этого используется стандартная функция `std::ranges::min_element`, которая эффективно находит наименьшее значение в диапазоне. +4. Найденное минимальное значение сохраняется в `result[i]`. + +Алгоритм имеет временную сложность O(M * N), так как требует полного обхода всех элементов матрицы. + +## 4. Схема распараллеливания + +Для распараллеливания алгоритма была выбрана технология MPI и схема "Мастер-Рабочий" (Master-Worker). + +- **Декомпозиция данных:** Задача декомпозируется по данным. Строки исходной матрицы равномерно распределяются между всеми доступными MPI-процессами. +- **Роли процессов:** + - **Процесс с рангом 0 (Мастер):** + 1. Владеет полной матрицей. + 2. Вычисляет, сколько строк должен обработать каждый процесс. Учитывает случай, когда общее число строк не делится нацело на число процессов, распределяя "остаток" по одному на первые процессы. + 3. Обрабатывает свою часть строк локально. + 4. Рассылает остальным процессам (Рабочим) их порции строк с помощью блокирующих отправок `MPI_Send`. + 5. После завершения вычислений всеми процессами, собирает частичные результаты (векторы минимальных значений) от всех, включая себя, с помощью `MPI_Gatherv`. Использование `Gatherv` необходимо, так как размеры порций у процессов могут незначительно отличаться. + - **Процессы с рангом > 0 (Рабочие):** + 1. Получают от Мастера информацию о количестве строк, которые им нужно обработать (`MPI_Recv`). + 2. В цикле получают данные каждой строки (`MPI_Recv`). + 3. Локально вычисляют минимальные значения для полученных строк. + 4. Отправляют свой вектор с результатами Мастеру в рамках коллективной операции `MPI_Gatherv`. + +- **Коммуникационная схема:** Используется комбинация сообщений точка-точка (`MPI_Send`/`MPI_Recv`) для распределения задач и коллективная операция (`MPI_Gatherv`) для сбора результатов. Это классический паттерн Scatter-Gather, реализованный вручную для гибкости. + +## 5. Экспериментальная установка + +- **Окружение:** + - **CPU:** 13th Gen Intel(R) Core(TM) i7-13700H (20 логических ядер) + - **RAM:** 8 GB (выделено для Docker-контейнера) + - **OS:** Ubuntu 24.04.2 LTS (внутри Docker-контейнера) +- **Инструментарий:** + - **Компилятор:** GCC 14.2.0 (g++) + - **MPI:** Open MPI 4.1.6 + - **Тип сборки:** `Release` +- **Параметры запуска:** + - Тесты производительности запускались для последовательной реализации и для MPI-реализации с числом процессов 4 и 8. + - Для тестов использовались случайно сгенерированные квадратные матрицы размерами 100x100, 1000x1000, 5000x5000 и 10000x10000. + +## 6. Результаты и обсуждение + +### 6.1 Корректность + +Корректность работы реализованных алгоритмов проверялась с помощью набора функциональных gtest-тестов. Тесты покрывают различные случаи, включая: +- Матрицы стандартного размера. +- Матрицы с отрицательными числами. +- Матрицы, состоящие из одной строки или одного столбца. + +Параллельная реализация на всех тестах выдавала идентичные результаты с последовательной, что подтверждает её корректность. + +### 6.2 Производительность + +Для оценки производительности измерялось чистое время выполнения алгоритма (`task_run`). На основе полученных данных были рассчитаны метрики ускорения (Speedup) и эффективности (Efficiency). + +- **Ускорение (S):** `S(p) = T(1) / T(p)`, где `T(1)` — время выполнения последовательной версии, а `T(p)` — время на `p` процессах. +- **Эффективность (E):** `E(p) = S(p) / p * 100%`. + +**Таблица 1. Результаты для матрицы 100x100** + +| Режим | Число процессов | Время, с | Ускорение | Эффективность | +| :---- | :--------------: | :------: | :-------: | :-----------: | +| seq | 1 | 0.000001 | 1.00 | N/A | +| mpi | 4 | 0.000042 | ~0.03 | ~0.8% | + +**Таблица 2. Результаты для матрицы 1000x1000** + +| Режим | Число процессов | Время, с | Ускорение | Эффективность | +| :---- | :--------------: | :------: | :-------: | :-----------: | +| seq | 1 | 0.000224 | 1.00 | N/A | +| mpi | 4 | 0.001155 | ~0.19 | ~4.8% | + +**Таблица 3. Результаты для матрицы 5000x5000** + +| Режим | Число процессов | Время, с | Ускорение | Эффективность | +| :---- | :--------------: | :------: | :-------: | :-----------: | +| seq | 1 | 0.007760 | 1.00 | N/A | +| mpi | 4 | 0.024518 | ~0.32 | ~8.0% | + +**Таблица 4. Результаты для матрицы 10000x10000** + +| Режим | Число процессов | Время, с | Ускорение | Эффективность | +| :---- | :--------------: | :------: | :-------: | :-----------: | +| seq | 1 | 0.030222 | 1.00 | N/A | +| mpi | 4 | 0.132926 | ~0.23 | ~5.7% | +| mpi | 8 | 0.733439 | ~0.04 | ~0.5% | + +**Результат будет выглядеть примерно так:** + +![Сравнение времени выполнения для матрицы 10000x10000](data/10000.png) + +**Анализ результатов:** + +Полученные результаты являются аномальными и демонстрируют **замедление** вместо ускорения при использовании MPI. Ускорение (Speedup) меньше единицы на всех тестах. Эффективность крайне низка. + +Возможные причины такого поведения: +1. **Накладные расходы MPI:** Для задач малого и среднего размера, которые выполняются очень быстро (в данном случае, за сотые и тысячные доли секунды), время, затрачиваемое на запуск MPI-процессов, установление коммуникаций, передачу данных (`MPI_Send`) и их сбор (`MPI_Gatherv`), многократно превышает время полезных вычислений. +2. **Высокопроизводительная последовательная версия:** Использование `std::ranges::min_element` в последовательной версии является очень эффективной операцией, которая, вероятно, хорошо векторизуется компилятором. +3. **Издержки на сериализацию/десериализацию:** Пересылка данных между процессами требует их упаковки и распаковки, что добавляет задержки. +4. **"Шум" измерений:** На очень малых интервалах времени (менее миллисекунды) точность измерений может страдать, однако общая тенденция к замедлению очевидна. + +Интересно отметить, что при увеличении числа процессов с 4 до 8 на самой большой задаче (10000x10000) время выполнения катастрофически выросло. Это указывает на то, что коммуникационные издержки стали доминирующим фактором, полностью "съев" выгоду от распараллеливания вычислений. + +## 8. Выводы + +В ходе работы были реализованы последовательный и параллельный (MPI) алгоритмы. Функциональное тестирование подтвердило их корректность. + +Однако, эксперименты с производительностью показали, что для данной конкретной задачи — поиска минимума в строке — параллелизация с помощью MPI на протестированных размерах данных является неэффективной. Накладные расходы на коммуникацию между процессами значительно превышают выигрыш от параллельных вычислений. + +Данный результат демонстрирует важный принцип параллельного программирования: не всякая задача получает выигрыш от распараллеливания. Эффективность сильно зависит от соотношения времени вычислений ко времени коммуникаций (computation-to-communication ratio). Для данной задачи это соотношение оказалось слишком низким. Чтобы MPI-реализация стала эффективной, необходимо либо значительно увеличить объем вычислений на каждую строку, либо использовать матрицу на порядки большего размера. + +## 9. Источники + +1. Parallel Programming Course - [https://learning-process.github.io/parallel_programming_course/ru/](https://learning-process.github.io/parallel_programming_course/ru/) +2. Parallel Programming 2025-2026 Video-Records - [https://disk.yandex.ru/d/NvHFyhOJCQU65w](https://disk.yandex.ru/d/NvHFyhOJCQU65w) +3. Open MPI: Documentation — [https://www.open-mpi.org/doc/](https://www.open-mpi.org/doc/) +4. C++ reference (cppreference.com) — [https://en.cppreference.com/w/cpp/algorithm/ranges/min_element](https://en.cppreference.com/w/cpp/algorithm/ranges/min_element) diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..bc6f68805c --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +class MinValueSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MinValueSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/seq/src/ops_seq.cpp b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..33d5c397d2 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/src/ops_seq.cpp @@ -0,0 +1,48 @@ +#include "makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +MinValueSEQ::MinValueSEQ(const InType &in) { + InType temp(in); + this->GetInput().swap(temp); + SetTypeOfTask(GetStaticTypeOfTask()); +} + +bool MinValueSEQ::ValidationImpl() { + const auto &mat = this->GetInput(); + if (mat.empty()) { + return false; + } + return std::ranges::all_of(mat, [](const auto &row) { return !row.empty(); }); +} + +bool MinValueSEQ::PreProcessingImpl() { + const auto &mat = this->GetInput(); + this->GetOutput().clear(); + this->GetOutput().resize(mat.size()); + return true; +} + +bool MinValueSEQ::RunImpl() { + const auto &mat = this->GetInput(); + auto &out = this->GetOutput(); + for (size_t i = 0; i < mat.size(); ++i) { + const auto &row = mat[i]; + if (!row.empty()) { + out[i] = *std::ranges::min_element(row); + } + } + return true; +} + +bool MinValueSEQ::PostProcessingImpl() { + return true; +} + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/settings.json b/tasks/makovskiy_i_min_value_in_matrix_rows/settings.json new file mode 100644 index 0000000000..2216442746 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "threads", + "tasks": { + "seq": "enabled", + "mpi": "enabled" + } +} diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/tests/functional/main.cpp b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/functional/main.cpp new file mode 100644 index 0000000000..32ec6d08fa --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/functional/main.cpp @@ -0,0 +1,76 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" +#include "makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp" +#include "makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +class MinValueRunFuncTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + const auto &input = std::get<0>(test_param); + const std::size_t rows = input.size(); + const std::size_t cols = input.empty() ? 0 : input.front().size(); + return "r" + std::to_string(rows) + "x" + std::to_string(cols); + } + + protected: + InType GetTestInputData() final { + auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + return std::get<0>(params); + } + + bool CheckTestOutputData(OutType &output_data) final { + if (ppc::util::IsUnderMpirun()) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank != 0) { + return true; + } + } + + auto params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + const auto &expected = std::get<1>(params); + return output_data == expected; + } +}; + +TEST_P(MinValueRunFuncTests, MinPerRow) { + ExecuteTest(GetParam()); +} + +namespace { + +const auto kTestCases = std::array{ + TestType{InType{{1, 2, 3}, {4, 5, 6}}, OutType{1, 4}}, + TestType{InType{{-1, 0}, {10, 2}, {7}}, OutType{-1, 2, 7}}, + TestType{InType{{5, 5, 5}}, OutType{5}}, + TestType{InType{{8}}, OutType{8}}, + + TestType{InType{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}}, OutType{1, 6, 11}}, + TestType{InType{{100, 200, 300}, {50, 60, 70}, {10, 20, 30}, {5, 6, 7}}, OutType{100, 50, 10, 5}}, +}; + +const auto kTasks = std::tuple_cat( + ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_makovskiy_i_min_value_in_matrix_rows), + ppc::util::AddFuncTask(kTestCases, PPC_SETTINGS_makovskiy_i_min_value_in_matrix_rows)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTasks); +const auto kPerfTestName = MinValueRunFuncTests::PrintFuncTestName; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, modernize-type-traits) +INSTANTIATE_TEST_SUITE_P(MinValueTests, MinValueRunFuncTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace makovskiy_i_min_value_in_matrix_rows diff --git a/tasks/makovskiy_i_min_value_in_matrix_rows/tests/performance/main.cpp b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/performance/main.cpp new file mode 100644 index 0000000000..7aa5958567 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/performance/main.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp" +#include "makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp" +#include "makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" +#include "util/include/util.hpp" + +namespace makovskiy_i_min_value_in_matrix_rows { + +class MinValuePerfTests : public ppc::util::BaseRunPerfTests { + protected: + InType GetTestInputData() final { + // constexpr std::size_t kRows = 100; + // constexpr std::size_t kCols = 100; + // constexpr std::size_t kRows = 1000; + // constexpr std::size_t kCols = 1000; + // constexpr std::size_t kRows = 5000; + // constexpr std::size_t kCols = 5000; + constexpr std::size_t kRows = 10000; + constexpr std::size_t kCols = 10000; + InType mat(kRows, std::vector(kCols)); + for (std::size_t i = 0; i < kRows; ++i) { + for (std::size_t j = 0; j < kCols; ++j) { + mat[i][j] = static_cast(i + j); + } + } + return mat; + } + + bool CheckTestOutputData(OutType &output_data) final { + if (ppc::util::IsUnderMpirun()) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank != 0) { + return true; + } + } + + // return output_data.size() == 100; + // return output_data.size() == 1000; + // return output_data.size() == 5000; + return output_data.size() == 10000; + } +}; + +TEST_P(MinValuePerfTests, RunPerfModes) { + ExecuteTest(GetParam()); +} + +namespace { + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_makovskiy_i_min_value_in_matrix_rows); +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = MinValuePerfTests::CustomPerfTestName; +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, modernize-type-traits) +INSTANTIATE_TEST_SUITE_P(MinValuePerf, MinValuePerfTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace makovskiy_i_min_value_in_matrix_rows