diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp new file mode 100644 index 0000000000..8949b922e5 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include + +#include "task/include/task.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +using InType = std::tuple>; +using OutType = std::vector; +using TestType = std::tuple>; +using BaseTask = ppc::task::Task; + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/data/test_matrix_3_3.txt b/tasks/kamaletdinov_r_max_matrix_rows_elem/data/test_matrix_3_3.txt new file mode 100644 index 0000000000..63eb41ef93 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/data/test_matrix_3_3.txt @@ -0,0 +1,4 @@ +3 3 +1 2 3 +4 5 6 +7 8 9 \ No newline at end of file diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/info.json b/tasks/kamaletdinov_r_max_matrix_rows_elem/info.json new file mode 100644 index 0000000000..e3adae504c --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Рамзан", + "last_name": "Камалетдинов", + "middle_name": "Рамилевич", + "group_number": "3823Б1ПР4", + "task_number": "1" + } +} diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..5b62515a62 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +class KamaletdinovRMaxMatrixRowsElemMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit KamaletdinovRMaxMatrixRowsElemMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + std::vector t_matrix_; + bool valid_ = false; +}; + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/src/ops_mpi.cpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..71c875f629 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/mpi/src/ops_mpi.cpp @@ -0,0 +1,173 @@ +#include "kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +namespace { +void PrepareScattervArrays(int mpi_size, std::size_t total_size, std::vector &sendcounts, + std::vector &displs) { + std::size_t procesess_step = total_size / mpi_size; + std::size_t remainder = total_size % mpi_size; + + for (int i = 0; i < mpi_size; i++) { + sendcounts[i] = static_cast(procesess_step); + if (std::cmp_less(i, remainder)) { + sendcounts[i]++; + } + displs[i] = (i == 0) ? 0 : displs[i - 1] + sendcounts[i - 1]; + } +} + +void ProcessLocalMatrix(const std::vector &local_matrix, std::size_t start, std::size_t end, std::size_t m, + std::vector &max_rows_elem) { + std::size_t row = start / m; + std::size_t local_idx = 0; + + if (!local_matrix.empty()) { + max_rows_elem[row] = local_matrix[local_idx]; + } + + for (std::size_t i = start; i < end; i++) { + if (i == ((row + 1) * m)) { + row++; + if (local_idx < local_matrix.size()) { + max_rows_elem[row] = local_matrix[local_idx]; + } + } + if (local_idx < local_matrix.size()) { + max_rows_elem[row] = std::max(max_rows_elem[row], local_matrix[local_idx]); + } + local_idx++; + } +} + +void MergeResults(int rank, int mpi_size, std::size_t n, std::vector &max_rows_elem) { + std::vector gathered_data; + std::vector recvcounts; + std::vector displs; + if (rank == 0) { + gathered_data.resize(n * mpi_size); + recvcounts.resize(mpi_size); + displs.resize(mpi_size); + for (int i = 0; i < mpi_size; i++) { + recvcounts[i] = static_cast(n); + displs[i] = static_cast(i * n); + } + } + MPI_Gatherv(max_rows_elem.data(), static_cast(n), MPI_INT, gathered_data.data(), recvcounts.data(), + displs.data(), MPI_INT, 0, MPI_COMM_WORLD); + if (rank == 0) { + for (std::size_t i = 0; i < n; i++) { + for (int j = 0; j < mpi_size; j++) { + max_rows_elem[i] = std::max(max_rows_elem[i], gathered_data[(j * n) + i]); + } + } + } + MPI_Bcast(max_rows_elem.data(), static_cast(n), MPI_INT, 0, MPI_COMM_WORLD); +} +} // namespace + +KamaletdinovRMaxMatrixRowsElemMPI::KamaletdinovRMaxMatrixRowsElemMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector(); +} + +bool KamaletdinovRMaxMatrixRowsElemMPI::ValidationImpl() { + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + std::vector &val = std::get<2>(GetInput()); + valid_ = (n > 0) && (m > 0) && (val.size() == (n * m)); + return valid_; +} + +bool KamaletdinovRMaxMatrixRowsElemMPI::PreProcessingImpl() { + if (valid_) { + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + std::vector &val = std::get<2>(GetInput()); + t_matrix_ = std::vector(n * m); + for (std::size_t i = 0; i < m; i++) { + for (std::size_t j = 0; j < n; j++) { + t_matrix_[(j * m) + i] = val[(i * n) + j]; + } + } + return true; + } + return false; +} + +bool KamaletdinovRMaxMatrixRowsElemMPI::RunImpl() { + // проверка корректности данных + if (!valid_) { + return false; + } + // получение размера матрицы + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + + // debug + // std::string deb = "\n\n-----------\n"; + // for(std::size_t i = 0; i < n; i++) { + // for(std::size_t j = 0; j < m; j++) { + // deb += std::to_string(t_matrix_[i*m + j]) + " "; + // } + // deb += "\n"; + // } + // std::cout << deb; + + // данные о процессе + int rank = 0; + int mpi_size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); + + // Подготовка массивов для MPI_Scatterv + std::vector sendcounts(mpi_size); + std::vector displs(mpi_size); + PrepareScattervArrays(mpi_size, t_matrix_.size(), sendcounts, displs); + + // Выделение памяти для локальной части матрицы + std::vector local_matrix(sendcounts[rank]); + + // Распределение матрицы с помощью MPI_Scatterv + MPI_Scatterv(t_matrix_.data(), sendcounts.data(), displs.data(), MPI_INT, local_matrix.data(), sendcounts[rank], + MPI_INT, 0, MPI_COMM_WORLD); + + // Обработка локальной части матрицы + std::size_t start = displs[rank]; + std::size_t end = start + sendcounts[rank]; + + // выделение памяти для сохранения максимального элемента + std::vector max_rows_elem(n, 0); + + ProcessLocalMatrix(local_matrix, start, end, m, max_rows_elem); + + // Объединение результатов от всех процессов + MergeResults(rank, mpi_size, n, max_rows_elem); + + // debug output + // std::cout << rank << ":"; + // for(std::size_t i = 0; i < n; i++) { + // std::cout << max_rows_elem[i] << " "; + // } + // std::cout << std::endl; + + GetOutput() = max_rows_elem; + + return true; +} + +bool KamaletdinovRMaxMatrixRowsElemMPI::PostProcessingImpl() { + return true; +} + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/report.md b/tasks/kamaletdinov_r_max_matrix_rows_elem/report.md new file mode 100644 index 0000000000..dbe553254a --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/report.md @@ -0,0 +1,310 @@ +# Нахождение максимальных значений по столбцам матрицы + +- **Студент**: Камалетдинов Рамзан Рамилевич, группа 3823Б1ПР4 +- **Технология**: SEQ | MPI +- **Вариант**: 16 + +## 1. Введение + +Матрицы - это фундаментальная структура данных, широко используемая в научных вычислениях. + +Цель моей работы - реализовать алгоритм поиска максимальных элементов по столбцам матрицы, распараллелить его при помощи технологии MPI для ускорения обработки больших матриц. + +## 2. Постановка задачи + +Дана матрица размером `m × n` + +Требуется найти максимальный элемент в каждой строке матрицы. + +Тип входных данных: + +```cpp +using InType = std::tuple>; +``` + +Тип выходных данных: + +```cpp +using OutType = std::vector; +``` + +Ограничения: + +- Матрица должна быть непустой (m > 0, n > 0) +- Элементы матрицы целочисленные + +## 3. Базовый алгоритм (последовательная версия) + +Для удобства работы с индексам и возможных оптимизаций при помощи векторизации, перед началом работы входящая матрица транспонируется. + +Алгоритм нахождения максимальных значений по **столбцам** матрицы превращается в нахождения максимальных значений по **строкам** матрицы. + +Алгоритм: + +- выделяется вектор размером `n` для сохранения результата каждой строки +- начинается стандратный цикл обхода всех элементов матрицы + - внешний цикл(i от 0 до `n`): инициализирует начальное значение в векторе результата для i-ой строки матрицы + - внутренний цикл (j от 0 до `m`): + - сравнение конкретного элемента строки с вектором результата + - если элемент оказался больше, то значение вектора заменяется на значение элемента + +Код алгоритма: + +```cpp +std::vector max_rows_elem(n); +for(std::size_t i = 0; i < n; i++) { + max_rows_elem[i] = t_matrix_[i * m]; + for(std::size_t j = 1; j < m; j++) { + if(max_rows_elem[i] < t_matrix_[i * m + j]){ + max_rows_elem[i] = t_matrix_[i * m + j]; + } + } +} +``` + +**Характеристики:** + +| Параметр | Значение | +| -------------------- | -------- | +| Сложность по времени | O(m x n) | +| Сложность по памяти | O(n) | + +## 4. Схема распараллеливания + +Расспараллеливание алгоритма организовано схемой распараллеливания по данным. +Исходная матрица, представленная в виде вектора, разбивается поровну между всеми процессами. + +Распределение данных происходит по следующему алгоритму. +**Код** + +```cpp +int rank = 0; +int mpi_size = 0; +MPI_Comm_rank(MPI_COMM_WORLD, &rank); +MPI_Comm_size(MPI_COMM_WORLD, &mpi_size); +//рассчет части матрицы для обработки процессом +std::size_t procesess_step = t_matrix_.size() / mpi_size; +std::size_t start = procesess_step * rank; +std::size_t end = procesess_step * (rank + 1); +if (rank == mpi_size - 1) { + end = t_matrix_.size(); +} +``` + +Кажддый процесс выполняет подсчет на своем участке: + +```cpp +//выделение памяти для сохранения максимального элемента +std::vector max_rows_elem; +if(rank == 0) { + max_rows_elem.resize(n * mpi_size, 0); +} else { + max_rows_elem.resize(n, 0); +} +std::size_t row = start / m; +max_rows_elem[row] = t_matrix_[start]; +for(std::size_t i = start; i < end; i++) { + if(i == (row + 1) * m) { + row++; + max_rows_elem[row] = t_matrix_[i]; + } + if(max_rows_elem[row] < t_matrix_[i]) { + max_rows_elem[row] = t_matrix_[i]; + } +} +``` + +Отличия параллельной версии: + +- память выделяется неодинаково на каждом процессе. Так как процесс с ранком 0 будет собирать полученные результаты со всех процессов, то размер вектора результата (`max_rows_elem`) больще остальных. +- Параллельный алгоритм дополнительно рассчитывает номер строки, с которой начинается его обход по элементам матрицы, чтобы корректно записывать данные в вектор результата. +- В цикле ушла вложенность, обход происходит прямиком по индексам +- Для опредление строки матрицы внутрь цикла добавлено условие перехода + +### Получение результата + +`MPI_Gather()` - собирает частичные результаты со всех процессов на процессе с ранком 0. +**Код** + +```cpp +MPI_Gather(max_rows_elem.data(), n, MPI_INT, max_rows_elem.data(), n, MPI_INT, 0, MPI_COMM_WORLD); +``` + +На процессе с ранком 0 происходит объединение результатов: +**Код** + +```cpp +if(rank == 0) { + for(std::size_t i = 0; i < n; i++) { + for(int j = 0; j < mpi_size; j++) { + if(max_rows_elem[i] < max_rows_elem[j * n + i]) { + max_rows_elem[i] = max_rows_elem[j * n + i]; + } + } + } +} +``` + +Полученный результат процесс с раном 0 отправляет всем остальным процессам при помощи вызова `MPI_Bcast()`. +**Код** + +```cpp +MPI_Bcast(max_rows_elem.data(), n, MPI_INT, 0, MPI_COMM_WORLD); +``` + +### Схема работы программы + +``` +┌──────────────────────────────┐ +│ Mатрица M x N │ +└────────┬─────────────────────┘ + │ (транспонирование матрицы) + ↓ +┌──────────────────────────────────┐ +│ Транспонированная матрица N x M │ +└────────┬─────────────────────────┘ + │ (распределение данных) + ↓ + ┌─────────────────┬─────────────────┬─────────────────┐ + │ Процесс 0 │ Процесс 1 │ Процесс 2 │ + │ start(0), end(0)│ start(1), end(1)│ start(2), end(2)│ + └───────┬─────────┴─────────────────┴─────────────────┘ + │ (локальный поиск максимумов) + ↓ + ┌───────────────────┬─────────────────────┬────────────────────┐ + │ local_vec_max(0) │ local_vec_max(1) │ local_vec_max(2) │ + └───────┬───────────┴─────────────────────┴────────────────────┘ + │ MPI_Gather + ↓ +┌───────────────────────────────────────┐ +│ Процесс 0 собирает все local_vec_max │ +└────────┬──────────────────────────────┘ + │ (объединение результатов) + ↓ +┌───────────────────────────────────────┐ +│ Процесс 0 вычисляет global_vec_max │ +└────────┬──────────────────────────────┘ + │ MPI_Bcast + ↓ +┌─────────────────────────────────────────────────┐ +│ Все процессы получают итоговые максимумы │ +│ Сохранение: GetOutput() = global_vec_max │ +└─────────────────────────────────────────────────┘ +``` + +## 5. Детали реализации + +**Структура проекта** +| Файл | Назначение | +|------------------------|---------------------------------------------| +| `common.hpp` | Определение входных и выходных типов задачи | +| `ops_seq.hpp/.cpp` | Последовательная реализация | +| `ops_mpi.hpp/.cpp` | MPI-реализация | +| `functional/main.cpp` | Функциональные тесты | +| `performance/main.cpp` | Тестирование производительности | + +**Особенности реализации** + +- Матрица предварительно транспонируется для более эффективного доступа к строкам + +## 6. Экспериментальная среда + +| Компонент | Значение | +| ---------- | --------------------------------------- | +| CPU | Apple M2 (8 cores) | +| RAM | 16 GB | +| ОС | OS: Ubuntu 24.04 (DevContainer / macOs) | +| Компилятор | GCC 13.3.0 (g++), C++20, CMake, Release | +| MPI | mpirun (Open MPI) 4.1.6 | + +Тестовые данные: + +- Данные из файла в формате: + - первая строка из двух чисел - размера матрицы (`m` `n`) + - остальные строки - значение элементов матрицы +- Сгенерированные матрицы рзаличных размеров + Для генерации случайных чисел было использована библиотека ``. + +## 7. Результаты и обсуждение + +### 7.1 Корректность + +Функциональные тесты используют как предопределенные входные файлы, так и сгенерированные данные. +Струкутра параметра теста: + +- строка: имя теста +- строка: путь к файлу или указание на генерацию +- целочисленный вектор:результат + +Тесты включают проверку: + +- Матрицы 3×3 с последовательными значениями +- Матриц различных размеров со случайными значениями +- Корректности вычисления максимальных элементов + +Обе реализации (`SEQ`, `MPI`) прошли тесты успешно. + +### 7.2 Производительность + +Рассчет производительности производился на сгененрированной матрице размером 10000 на 10000, с сидом генерации 123. + +| Mode | Count | Time, s | Speedup | Efficiency | +| ---- | ----- | ------- | ------- | ---------- | +| seq | 1 | 0.0642 | 1.00 | N/A | +| omp | 2 | 0.0325 | 1.98 | 98% | +| omp | 4 | 0.0225 | 2.83 | 70.75% | + +## 8. Заключение + +В ходе выполнения работы мне пришлось: + +- реализовать алгоритм нахождения максимальных значений по столбцам матрицы +- распараллелить его при помощи MPI +- разработать систему тестирования для проверки корректности реализации +- провести измерения производительности и рассчитать характеристики ускорения и эффективности + +## 9. Источники + +## Приложение + +Код транспонирования матрицы: + +```cpp +std::size_t m = std::get<0>(GetInput()); +std::size_t n = std::get<1>(GetInput()); +std::vector &val = std::get<2>(GetInput()); +t_matrix_ = std::vector(n * m); +for(std::size_t i = 0; i < m; i++) { + for(std::size_t j = 0; j < n; j++) { + t_matrix_[(j * m) + i] = val[(i * n) + j]; + } +} +``` + +Код генерации тестовых данных + +```cpp +void Generate(std::size_t m, std::size_t n, int seed) { + std::mt19937 gen(seed); + std::uniform_int_distribution<> idis(-10, 20); + std::vector val(m * n); + std::vector answer(n); + // задание начальных значений для ответа + // первая строка матрицы задает максимальные значнечения для элементов столбцов + for (std::size_t i = 0; i < n; i++) { + val[i] = idis(gen); + answer[i] = val[i]; + } + // генерация остальной матрицы, вектора ответа + for (std::size_t i = 1; i < m; i++) { + for (std::size_t j = 0; j < n; j++) { + val[i * n + j] = idis(gen); + if (answer[j] < val[i * n + j]) { + answer[j] = val[i * n + j]; + } + } + } + input_data_ = std::make_tuple(m, n, val); + correct_test_output_data_ = answer; +} +``` diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..69d549177d --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +class KamaletdinovRMaxMatrixRowsElemSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit KamaletdinovRMaxMatrixRowsElemSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + std::vector t_matrix_; + bool valid_ = false; +}; + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/src/ops_seq.cpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..4c00ead8aa --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/seq/src/ops_seq.cpp @@ -0,0 +1,80 @@ +#include "kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { +KamaletdinovRMaxMatrixRowsElemSEQ::KamaletdinovRMaxMatrixRowsElemSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector(); +} + +bool KamaletdinovRMaxMatrixRowsElemSEQ::ValidationImpl() { + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + std::vector &val = std::get<2>(GetInput()); + valid_ = (n > 0) && (m > 0) && (val.size() == (n * m)); + return valid_; +} + +bool KamaletdinovRMaxMatrixRowsElemSEQ::PreProcessingImpl() { + if (valid_) { + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + std::vector &val = std::get<2>(GetInput()); + t_matrix_ = std::vector(n * m); + for (std::size_t i = 0; i < m; i++) { + for (std::size_t j = 0; j < n; j++) { + t_matrix_[(j * m) + i] = val[(i * n) + j]; + } + } + return true; + } + return false; +} + +bool KamaletdinovRMaxMatrixRowsElemSEQ::RunImpl() { + if (!valid_) { + return false; + } + std::size_t m = std::get<0>(GetInput()); + std::size_t n = std::get<1>(GetInput()); + + // debug + // std::string deb = "\n\n----\n"; + // for(std::size_t i = 0; i < n; i++) { + // for(std::size_t j = 0; j < m; j++) { + // deb += std::to_string(t_matrix_[i*m + j]) + " "; + // } + // deb += "\n"; + // } + // std::cout << deb; + + std::vector max_rows_elem(n); + for (std::size_t i = 0; i < n; i++) { + max_rows_elem[i] = t_matrix_[(i * m)]; + for (std::size_t j = 1; j < m; j++) { + max_rows_elem[i] = std::max(max_rows_elem[i], t_matrix_[(i * m) + j]); + } + } + + // debug output + // std::cout << "seq" << ":"; + // for(std::size_t i = 0; i < n; i++) { + // std::cout << max_rows_elem[i] << " "; + // } + // std::cout << std::endl; + + GetOutput() = max_rows_elem; + return true; +} + +bool KamaletdinovRMaxMatrixRowsElemSEQ::PostProcessingImpl() { + return true; +} + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/settings.json b/tasks/kamaletdinov_r_max_matrix_rows_elem/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/.clang-tidy b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/.clang-tidy @@ -0,0 +1,13 @@ +InheritParentConfig: true + +Checks: > + -modernize-loop-convert, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-non-const-global-variables, + -misc-use-anonymous-namespace, + -modernize-use-std-print, + -modernize-type-traits + +CheckOptions: + - key: readability-function-cognitive-complexity.Threshold + value: 50 # Relaxed for tests diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/functional/main.cpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/functional/main.cpp new file mode 100644 index 0000000000..7ab926dad7 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/functional/main.cpp @@ -0,0 +1,146 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" +#include "kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp" +#include "kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +class KamaletdinovRMaxMatrixRowsElemTests : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::get<0>(test_param); + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + if (!std::get<1>(params).empty()) { + GetDataFromFile(params); + } else { + Generate(params); + } + } + + bool CheckTestOutputData(OutType &output_data) final { + // реализована не стандратная проверка, + // так как вектор ответа в процессе с ранком 0 имеет больший размер + // для уменьшения времени на выделение лишней памяти + for (std::size_t i = 0; i < correct_test_output_data_.size(); i++) { + if (output_data[i] != correct_test_output_data_[i]) { + return false; + } + } + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; + std::vector correct_test_output_data_; + + void Generate(const TestType ¶ms) { + std::size_t m = std::get<2>(params)[0]; + std::size_t n = std::get<2>(params)[1]; + int seed = std::get<2>(params)[2]; + + std::mt19937 gen(seed); + std::uniform_int_distribution<> idis(-10, 20); + + std::vector val(m * n); + std::vector answer(n); + // задание начальных значений для ответа + // первая строка матрицы задает максимальные значнечения для элементов столбцов + for (std::size_t i = 0; i < n; i++) { + val[i] = idis(gen); + answer[i] = val[i]; + } + // генерация остальной матрицы, вектора ответа + for (std::size_t i = 1; i < m; i++) { + for (std::size_t j = 0; j < n; j++) { + val[(i * n) + j] = idis(gen); + answer[j] = std::max(answer[j], val[(i * n) + j]); + } + } + input_data_ = std::make_tuple(m, n, val); + correct_test_output_data_ = answer; + + // debug output + // std::string deb = "\n\n-----------\n"; + // for(std::size_t i = 0; i < m; i++) { + // for(std::size_t j = 0; j < n; j++) { + // deb += std::to_string(val[i*n + j]) + " "; + // } + // deb += "\n"; + // } + // std::cout << deb; + // std::cout << "----------\n"; + // for(std::size_t i = 0; i < n; i++) { + // std::cout << answer[i] << " "; + // } + // std::cout << std::endl; + } + + void GetDataFromFile(const TestType ¶ms) { + std::size_t m = 0; + std::size_t n = 0; + std::string local = std::get<1>(params) + ".txt"; + std::string abs_path = ppc::util::GetAbsoluteTaskPath(PPC_ID_kamaletdinov_r_max_matrix_rows_elem, local); + std::ifstream file(abs_path); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file: " + abs_path); + } + file >> m; + file >> n; + std::vector val(m * n); + for (std::size_t i = 0; i < val.size(); i++) { + file >> val[i]; + } + input_data_ = std::make_tuple(m, n, val); + correct_test_output_data_ = std::get<2>(params); + } +}; + +namespace { + +TEST_P(KamaletdinovRMaxMatrixRowsElemTests, MatmulFromPic) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple("Matrix_3_3_from_1_to_9", "test_matrix_3_3", std::vector({7, 8, 9})), + std::make_tuple("Generate_7_7", "", std::vector({7, 7, 123})), + std::make_tuple("Generate_1000_1000", "", std::vector({1000, 1000, 123})), + std::make_tuple("Generate_77_88", "", std::vector({77, 88, 123})), + std::make_tuple("Generate_7_8", "", std::vector({7, 8, 123}))}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_kamaletdinov_r_max_matrix_rows_elem), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_kamaletdinov_r_max_matrix_rows_elem)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = KamaletdinovRMaxMatrixRowsElemTests::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(PicMatrixTests, KamaletdinovRMaxMatrixRowsElemTests, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace kamaletdinov_r_max_matrix_rows_elem diff --git a/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/performance/main.cpp b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/performance/main.cpp new file mode 100644 index 0000000000..732210d213 --- /dev/null +++ b/tasks/kamaletdinov_r_max_matrix_rows_elem/tests/performance/main.cpp @@ -0,0 +1,90 @@ +#include + +#include +#include +#include +#include +#include + +#include "kamaletdinov_r_max_matrix_rows_elem/common/include/common.hpp" +#include "kamaletdinov_r_max_matrix_rows_elem/mpi/include/ops_mpi.hpp" +#include "kamaletdinov_r_max_matrix_rows_elem/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace kamaletdinov_r_max_matrix_rows_elem { + +class KamaletdinovRMaxMatrixRowsElemPerfTest : public ppc::util::BaseRunPerfTests { + std::vector correct_test_output_data_; + InType input_data_; + + void SetUp() override { + Generate(10000, 10000, 123); + } + + bool CheckTestOutputData(OutType &output_data) final { + for (std::size_t i = 0; i < correct_test_output_data_.size(); i++) { + if (output_data[i] != correct_test_output_data_[i]) { + return false; + } + } + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + void Generate(std::size_t m, std::size_t n, int seed) { + std::mt19937 gen(seed); + std::uniform_int_distribution<> idis(-10, 20); + + std::vector val(m * n); + std::vector answer(n); + // задание начальных значений для ответа + // первая строка матрицы задает максимальные значнечения для элементов столбцов + for (std::size_t i = 0; i < n; i++) { + val[i] = idis(gen); + answer[i] = val[i]; + } + // генерация остальной матрицы, вектора ответа + for (std::size_t i = 1; i < m; i++) { + for (std::size_t j = 0; j < n; j++) { + val[(i * n) + j] = idis(gen); + answer[j] = std::max(answer[j], val[(i * n) + j]); + } + } + input_data_ = std::make_tuple(m, n, val); + correct_test_output_data_ = answer; + + // debug output + // std::string deb = "\n\n-----------\n"; + // for(std::size_t i = 0; i < m; i++) { + // for(std::size_t j = 0; j < n; j++) { + // deb += std::to_string(val[i*n + j]) + " "; + // } + // deb += "\n"; + // } + // std::cout << deb; + // std::cout << "----------\n"; + // for(std::size_t i = 0; i < n; i++) { + // std::cout << answer[i] << " "; + // } + // std::cout << std::endl; + } +}; + +TEST_P(KamaletdinovRMaxMatrixRowsElemPerfTest, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_kamaletdinov_r_max_matrix_rows_elem); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = KamaletdinovRMaxMatrixRowsElemPerfTest::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunModeTests, KamaletdinovRMaxMatrixRowsElemPerfTest, kGtestValues, kPerfTestName); + +} // namespace kamaletdinov_r_max_matrix_rows_elem