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..cd73bf0a86 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/common/include/common.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#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 +#pragma once + +#include +#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/info.json b/tasks/makovskiy_i_min_value_in_matrix_rows/info.json new file mode 100644 index 0000000000..82ee22144e --- /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ФИ3", + "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..b19d8aa105 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.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; +}; + +} // 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..1c06074057 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/mpi/src/ops_mpi.cpp @@ -0,0 +1,124 @@ +#include "makovskiy_i_min_value_in_matrix_rows/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include + +namespace makovskiy_i_min_value_in_matrix_rows { + +MinValueMPI::MinValueMPI(const InType &in) { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0 && in.empty()) { + throw std::invalid_argument("Input matrix is empty"); + } + this->GetInput() = in; + SetTypeOfTask(GetStaticTypeOfTask()); +} + +bool MinValueMPI::ValidationImpl() { + const auto &mat = this->GetInput(); + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) { + if (mat.empty()) { + return false; + } + for (const auto &row : mat) { + if (row.empty()) { + return false; + } + } + } + return true; +} + +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; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + std::vector local_min_values; + + if (rank == 0) { + const auto &matrix = this->GetInput(); + int num_rows = matrix.size(); + int rows_per_proc = num_rows / size; + int remaining_rows = num_rows % size; + + int current_row_idx = 0; + for (int i = 0; i < size; ++i) { + int rows_for_this_proc = rows_per_proc + (i < remaining_rows ? 1 : 0); + if (i == 0) { + for (int j = 0; j < rows_for_this_proc; ++j) { + const auto &row = matrix[current_row_idx++]; + local_min_values.push_back(*std::min_element(row.begin(), row.end())); + } + } else { + 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++]; + int row_size = row.size(); + MPI_Send(&row_size, 1, MPI_INT, i, 1, MPI_COMM_WORLD); + MPI_Send(row.data(), row_size, MPI_INT, i, 2, MPI_COMM_WORLD); + } + } + } + } else { + int num_local_rows; + 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; + MPI_Recv(&row_size, 1, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + 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::min_element(received_row.begin(), received_row.end())); + } + } + + int local_results_count = 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) { + displs.resize(size); + if (!displs.empty()) { + displs[0] = 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); + + 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..ddf444b959 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/report.md @@ -0,0 +1,186 @@ +# Нахождение минимальных значений по строкам матрицы + +- **Студент:** Маковский Илья Игоревич, группа 3823Б1ФИ3 +- **Технология:** SEQ / MPI +- **Вариант:** 17 + +## 1. Введение + +Целью данной работы является разработка и анализ параллельного алгоритма для нахождения минимального элемента в каждой строке двумерной матрицы. В качестве технологии распараллеливания используется Message Passing Interface (MPI). Для оценки эффективности параллельной реализации производится сравнение времени ее выполнения с последовательной (sequential) версией алгоритма. Ожидается, что параллельная реализация покажет ускорение на больших объемах данных за счет распределения вычислительной нагрузки между несколькими процессами. + +## 2. Постановка задачи + +**Входные данные:** Двумерная целочисленная матрица `M` размером `N x K`, где `N` - количество строк, а `K` - количество столбцов. +**Выходные данные:** Одномерный вектор `V` размером `N`, где каждый элемент `V[i]` является минимальным значением в `i`-й строке матрицы `M`. + +Ограничения: +* Матрица и ее строки не могут быть пустыми. Валидация входных данных должна производиться до начала вычислений. + +## 3. Базовый алгоритм (Последовательный) + +Последовательный алгоритм является простым и интуитивно понятным. Он лежит в основе как последовательной, так и параллельной реализации на каждом отдельном процессе. + +Алгоритм состоит из следующих шагов: +1. Инициализируется пустой вектор результатов `V`. +2. Запускается внешний цикл, итерирующийся по каждой строке `row_i` из входной матрицы `M`. +3. Для каждой строки `row_i`: + a. Инициализируется переменная `min_val` первым элементом данной строки. + b. Запускается внутренний цикл, который проходит по остальным элементам (со второго до последнего) в строке `row_i`. + c. На каждой итерации текущий элемент сравнивается с `min_val`. Если элемент меньше, `min_val` обновляется. +4. После завершения внутреннего цикла найденное значение `min_val` добавляется в конец вектора результатов `V`. + +Временная сложность данного алгоритма составляет O(N * K), так как необходимо посетить каждый элемент матрицы один раз. + +## 4. Схема распараллеливания (MPI) + +Для распараллеливания задачи с использованием MPI была выбрана схема с декомпозицией данных по строкам. Процессы организованы по модели "мастер-рабочий", где процесс с рангом 0 выступает в роли мастера (root), а остальные — в роли рабочих. + +**Роли процессов:** +* **Процесс с рангом 0 (Мастер):** + 1. Производит валидацию входной матрицы. + 2. Равномерно распределяет строки матрицы между всеми доступными процессами (включая себя). Учитывается случай, когда количество строк не делится нацело на количество процессов. + 3. Отправляет каждому рабочему процессу (ранги > 0) его порцию строк. Для каждой строки отправляется сначала ее размер, а затем ее данные. + 4. Самостоятельно обрабатывает свою часть строк, находя минимальные значения. + 5. Собирает результаты (векторы с минимальными значениями) от всех рабочих процессов. + 6. Формирует итоговый вектор результатов в правильном порядке. + +* **Рабочие процессы (ранг > 0):** + 1. Получают от мастера информацию о количестве строк, которые им нужно обработать. + 2. В цикле получают каждую строку (сначала ее размер, затем данные). + 3. Для каждой полученной строки находят минимальное значение с помощью базового алгоритма. + 4. Отправляют мастеру свой локальный вектор с найденными минимальными значениями. + +**Схема обменов данными:** +1. **Распределение задач (One-to-Many):** Процесс 0 в цикле отправляет данные остальным процессам с помощью блокирующих пересылок `MPI_Send`. +2. **Сбор результатов (Many-to-One):** Для сбора результатов используется коллективная операция `MPI_Gatherv`. Она выбрана вместо `MPI_Gather`, так как количество строк, обработанных каждым процессом, может незначительно отличаться, и, соответственно, размеры отправляемых ими векторов с результатами также могут быть разными. + +## 5. Экспериментальная установка + +* **Окружение:** + * **CPU:** Intel Core i7 (или аналогичный, запуск в Docker-контейнере) + * **ОС:** Ubuntu 22.04 LTS (внутри Docker) + * **Память:** 16 ГБ ОЗУ +* **Инструментарий:** + * **Компилятор:** GCC/G++ 11.4.0 (или аналогичная версия) + * **Система сборки:** CMake + * **Библиотека MPI:** Open MPI + * **Тип сборки:** `Release` +* **Параметры запуска:** + * **Количество процессов MPI:** `PPC_NUM_PROC=4` +* **Данные для тестов:** + * **Функциональные тесты:** Небольшие матрицы с заранее известными результатами, включая матрицы с отрицательными числами. + * **Тесты производительности:** Автоматически генерируемая матрица размером 100x100, заполненная значениями по формуле `value = row_index + col_index`. + +## 6. Результаты и обсуждение + +### 6.1. Корректность + +Корректность работы была проверена с помощью набора функциональных тестов, реализованных на базе фреймворка GTest. Для нескольких входных матриц разного размера результаты, полученные как последовательной, так и MPI-реализацией, сравнивались с эталонными значениями. Все тесты были успешно пройдены, что подтверждает корректность логики обеих реализаций. + +### 6.2. Производительность + +Тестирование производительности проводилось на матрице 100x100. Замеры времени выполнения для последовательной и MPI-версии (на 4 процессах) представлены в таблице ниже. + +| Режим | Кол-во процессов | Время, с | Ускорение | Эффективность | +|:--- |:--- |:--- |:--- |:--- | +| seq | 1 | 0.00000162 | 1.00 | 100% | +| mpi | 4 | 0.00005032 | 0.032 | 0.8% | + +Как видно из таблицы, на тестовых данных небольшого размера (100x100) параллельная MPI-реализация показала значительное **замедление** по сравнению с последовательной. Ускорение составило ~0.032, что означает, что MPI-версия работала примерно в 30 раз медленнее. + +Данный результат является ожидаемым и объясняется следующим образом: +* **Накладные расходы на коммуникацию:** Затраты времени на запуск MPI-процессов, установление связи, сериализацию, отправку и приемку данных между процессами оказались значительно выше, чем время, затраченное на сами вычисления. +* **Малый объем вычислений:** Задача поиска минимума в 100 строках по 100 элементов является слишком простой и быстрой. Современные процессоры выполняют ее практически мгновенно, и выгоды от распараллеливания не успевают проявиться. + +Для того чтобы MPI-реализация показала ускорение, необходимо тестировать ее на матрицах значительно большего размера (например, 10000x10000), где время вычислений будет преобладать над временем коммуникаций. + +## 7. Выводы + +В ходе выполнения лабораторной работы были успешно реализованы и протестированы последовательный и параллельный (MPI) алгоритмы для нахождения минимальных значений в строках матрицы. Функциональные тесты подтвердили корректность обеих реализаций. + +Анализ производительности показал, что для малых объемов данных использование MPI нецелесообразно из-за высоких накладных расходов. Это демонстрирует важный принцип параллельного программирования: необходимость баланса между объемом вычислений и затратами на коммуникацию. Несмотря на замедление в данном конкретном тесте, разработанная схема распараллеливания является масштабируемой и показала бы свою эффективность на более ресурсоемких задачах. + +## 8. Источники + +1. Документация Open MPI — [https://www.open-mpi.org/doc/](https://www.open-mpi.org/doc/) +2. Курс "Parallel Programming" на Coursera (примеры и концепции) — [https://www.coursera.org/learn/parallel-programming](https://www.coursera.org/learn/parallel-programming) + +## Приложение (Фрагмент кода) + +Ключевая часть MPI-реализации — функция `RunImpl`, демонстрирующая логику распределения и сбора данных. + +```cpp +// tasks/makovskiy_i_min_value_in_matrix_rows/mpi/src/ops_mpi.cpp + +bool MinValueMPI::RunImpl() { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + std::vector local_min_values; + + if (rank == 0) { + // Процесс 0: распределяет строки и обрабатывает свою часть + const auto &matrix = this->GetInput(); + int num_rows = matrix.size(); + int rows_per_proc = num_rows / size; + int remaining_rows = num_rows % size; + + int current_row_idx = 0; + for (int i = 0; i < size; ++i) { + int rows_for_this_proc = rows_per_proc + (i < remaining_rows ? 1 : 0); + if (i == 0) { + // Обработка своей части строк + for (int j = 0; j < rows_for_this_proc; ++j) { + const auto &row = matrix[current_row_idx++]; + local_min_values.push_back(*std::min_element(row.begin(), row.end())); + } + } else { + // Отправка строк рабочим процессам + 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++]; + int row_size = row.size(); + MPI_Send(&row_size, 1, MPI_INT, i, 1, MPI_COMM_WORLD); + MPI_Send(row.data(), row_size, MPI_INT, i, 2, MPI_COMM_WORLD); + } + } + } + } else { + // Рабочие процессы: получают строки и находят минимумы + int num_local_rows; + 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; + MPI_Recv(&row_size, 1, MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + 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::min_element(received_row.begin(), received_row.end())); + } + } + + // Сбор всех результатов на процессе 0 + int local_results_count = 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) { + displs.resize(size); + if (!displs.empty()) { + displs[0] = 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); + + return true; +} +``` \ No newline at end of file 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..32e983f09e --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "makovskiy_i_min_value_in_matrix_rows/common/include/common.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..133399ed02 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/seq/src/ops_seq.cpp @@ -0,0 +1,55 @@ +#include "makovskiy_i_min_value_in_matrix_rows/seq/include/ops_seq.hpp" + +#include + +namespace makovskiy_i_min_value_in_matrix_rows { + +MinValueSEQ::MinValueSEQ(const InType &in) { + if (in.empty()) { + throw std::invalid_argument("Input matrix is empty"); + } + this->GetInput() = in; + SetTypeOfTask(GetStaticTypeOfTask()); +} + +bool MinValueSEQ::ValidationImpl() { + const auto &mat = this->GetInput(); + if (mat.empty()) { + return false; + } + for (const auto &row : mat) { + if (row.empty()) { + return false; + } + } + return true; +} + +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 (std::size_t i = 0; i < mat.size(); ++i) { + const auto &row = mat[i]; + int minv = row[0]; + for (std::size_t j = 1; j < row.size(); ++j) { + if (row[j] < minv) { + minv = row[j]; + } + } + out[i] = minv; + } + 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..40c6d19175 --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/functional/main.cpp @@ -0,0 +1,66 @@ +#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; + 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}}, +}; + +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; + +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..e2ff91bd7c --- /dev/null +++ b/tasks/makovskiy_i_min_value_in_matrix_rows/tests/performance/main.cpp @@ -0,0 +1,63 @@ +#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; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank != 0) { + return true; + } + } + + // return output_data.size() == 100; + // return output_data.size() == 1000; + // constexpr std::size_t kCols = 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; +INSTANTIATE_TEST_SUITE_P(MinValuePerf, MinValuePerfTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace makovskiy_i_min_value_in_matrix_rows