diff --git a/tasks/morozova_s_connected_components/common/include/common.hpp b/tasks/morozova_s_connected_components/common/include/common.hpp new file mode 100644 index 0000000000..dce5501ba8 --- /dev/null +++ b/tasks/morozova_s_connected_components/common/include/common.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace morozova_s_connected_components { +using InType = std::vector>; +using OutType = std::vector>; +using TestType = std::tuple; +using BaseTask = ppc::task::Task; +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/info.json b/tasks/morozova_s_connected_components/info.json new file mode 100644 index 0000000000..2e45589574 --- /dev/null +++ b/tasks/morozova_s_connected_components/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Софья", + "last_name": "Морозова", + "middle_name": "Андреевна", + "group_number": "3823Б1ФИ2", + "task_number": "3" + } +} diff --git a/tasks/morozova_s_connected_components/mpi/include/ops_mpi.hpp b/tasks/morozova_s_connected_components/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..43f33c99e6 --- /dev/null +++ b/tasks/morozova_s_connected_components/mpi/include/ops_mpi.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +#include "morozova_s_connected_components/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace morozova_s_connected_components { + +class MorozovaSConnectedComponentsMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + + explicit MorozovaSConnectedComponentsMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + void FloodFill(int row, int col, int label); + [[nodiscard]] std::vector> GetNeighbors(int row, int col); + + void InitMPI(); + [[nodiscard]] std::pair ComputeRowRange() const; + void ComputeLocalComponents(int start_row, int end_row, int base_label); + void GatherLocalResults(); + void MergeBoundaries(); + void ProcessBoundaryCell(int proc, int j, int dj, std::unordered_map &parent); + static int FindRoot(std::unordered_map &parent, int v); + void NormalizeLabels(); + void BroadcastResult(); + void SendLocalResult(int start_row, int end_row); + void ReceiveFinalResult(); + + int rank_{0}; + int size_{1}; + int rows_{0}; + int cols_{0}; + int rows_per_proc_{0}; + int remainder_{0}; +}; + +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/mpi/src/ops_mpi.cpp b/tasks/morozova_s_connected_components/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..72aa2e3d7c --- /dev/null +++ b/tasks/morozova_s_connected_components/mpi/src/ops_mpi.cpp @@ -0,0 +1,301 @@ +#include "morozova_s_connected_components/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "morozova_s_connected_components/common/include/common.hpp" + +namespace morozova_s_connected_components { + +namespace { +constexpr int kLabelOffset = 1000000; + +constexpr std::array, 8> kShifts = { + {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}}; +} // namespace + +MorozovaSConnectedComponentsMPI::MorozovaSConnectedComponentsMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool MorozovaSConnectedComponentsMPI::ValidationImpl() { + const auto &input = GetInput(); + if (input.empty()) { + return true; + } + const size_t cols = input.front().size(); + for (const auto &row : input) { + if (row.size() != cols) { + return false; + } + for (int v : row) { + if (v != 0 && v != 1) { + return false; + } + } + } + return true; +} + +bool MorozovaSConnectedComponentsMPI::PreProcessingImpl() { + rows_ = static_cast(GetInput().size()); + + if (rows_ == 0) { + cols_ = 0; + GetOutput().clear(); + return true; + } + + cols_ = static_cast(GetInput().front().size()); + GetOutput().assign(rows_, std::vector(cols_, 0)); + return true; +} + +void MorozovaSConnectedComponentsMPI::InitMPI() { + MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + MPI_Comm_size(MPI_COMM_WORLD, &size_); + rows_per_proc_ = rows_ / size_; + remainder_ = rows_ % size_; +} + +std::pair MorozovaSConnectedComponentsMPI::ComputeRowRange() const { + const int start = (rank_ * rows_per_proc_) + std::min(rank_, remainder_); + const int end = start + rows_per_proc_ + (rank_ < remainder_ ? 1 : 0); + return {start, end}; +} + +std::vector> MorozovaSConnectedComponentsMPI::GetNeighbors(int row, int col) { + std::vector> neighbors; + const auto &input = GetInput(); + + for (const auto &[dr, dc] : kShifts) { + const int nr = row + dr; + const int nc = col + dc; + if (nr >= 0 && nr < rows_ && nc >= 0 && nc < cols_ && input[nr][nc] == 1) { + neighbors.emplace_back(nr, nc); + } + } + return neighbors; +} + +void MorozovaSConnectedComponentsMPI::FloodFill(int row, int col, int label) { + auto &output = GetOutput(); + + std::vector> visited(rows_, std::vector(cols_, false)); + std::queue> q; + q.emplace(row, col); + visited[row][col] = true; + output[row][col] = label; + + while (!q.empty()) { + const auto [r, c] = q.front(); + q.pop(); + for (const auto &[nr, nc] : GetNeighbors(r, c)) { + if (!visited[nr][nc]) { + visited[nr][nc] = true; + output[nr][nc] = label; + q.emplace(nr, nc); + } + } + } +} + +void MorozovaSConnectedComponentsMPI::ComputeLocalComponents(int start_row, int end_row, int base_label) { + const auto &input = GetInput(); + auto &output = GetOutput(); + + std::vector> visited(rows_, std::vector(cols_, false)); + int local_label = 1; + + for (int i = start_row; i < end_row; ++i) { + for (int j = 0; j < cols_; ++j) { + if (input[i][j] == 1 && output[i][j] == 0) { + FloodFill(i, j, base_label + local_label); + ++local_label; + } + } + } +} + +void MorozovaSConnectedComponentsMPI::GatherLocalResults() { + auto &output = GetOutput(); + + for (int proc = 1; proc < size_; ++proc) { + const int ps = (proc * rows_per_proc_) + std::min(proc, remainder_); + const int pe = ps + rows_per_proc_ + (proc < remainder_ ? 1 : 0); + const int pr = pe - ps; + + std::vector buf(static_cast(pr) * static_cast(cols_)); + MPI_Recv(buf.data(), static_cast(buf.size()), MPI_INT, proc, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + for (int i = 0; i < pr; ++i) { + for (int j = 0; j < cols_; ++j) { + const size_t idx = (static_cast(i) * static_cast(cols_)) + static_cast(j); + output[ps + i][j] = buf[idx]; + } + } + } +} + +void MorozovaSConnectedComponentsMPI::ProcessBoundaryCell(int proc, int j, int dj, + std::unordered_map &parent) { + const auto &input = GetInput(); + const auto &output = GetOutput(); + + const int br = (proc * rows_per_proc_) + std::min(proc, remainder_); + if (br <= 0 || br >= rows_) { + return; + } + + const int nj = j + dj; + if (nj < 0 || nj >= cols_) { + return; + } + + if (input[br - 1][j] == 1 && input[br][nj] == 1) { + const int a = output[br - 1][j]; + const int b = output[br][nj]; + if (a != b) { + parent[std::max(a, b)] = std::min(a, b); + } + } +} + +int MorozovaSConnectedComponentsMPI::FindRoot(std::unordered_map &parent, int v) { + while (parent.contains(v)) { + v = parent[v]; + } + return v; +} + +void MorozovaSConnectedComponentsMPI::MergeBoundaries() { + auto &output = GetOutput(); + std::unordered_map parent; + + for (int proc = 1; proc < size_; ++proc) { + const int br = (proc * rows_per_proc_) + std::min(proc, remainder_); + if (br <= 0 || br >= rows_) { + continue; + } + + for (int j = 0; j < cols_; ++j) { + ProcessBoundaryCell(proc, j, -1, parent); + ProcessBoundaryCell(proc, j, 0, parent); + ProcessBoundaryCell(proc, j, 1, parent); + } + } + + for (int i = 0; i < rows_; ++i) { + for (int j = 0; j < cols_; ++j) { + int &v = output[i][j]; + if (v > 0) { + v = FindRoot(parent, v); + } + } + } +} + +void MorozovaSConnectedComponentsMPI::NormalizeLabels() { + auto &output = GetOutput(); + std::unordered_map remap; + int next = 1; + for (auto &row : output) { + for (int &v : row) { + if (v > 0) { + if (!remap.contains(v)) { + remap[v] = next++; + } + v = remap[v]; + } + } + } +} + +void MorozovaSConnectedComponentsMPI::BroadcastResult() { + auto &output = GetOutput(); + std::vector flat(static_cast(rows_) * static_cast(cols_)); + for (int i = 0; i < rows_; ++i) { + for (int j = 0; j < cols_; ++j) { + const size_t idx = (static_cast(i) * static_cast(cols_)) + static_cast(j); + flat[idx] = output[i][j]; + } + } + + for (int proc = 1; proc < size_; ++proc) { + MPI_Send(flat.data(), static_cast(flat.size()), MPI_INT, proc, 1, MPI_COMM_WORLD); + } +} + +void MorozovaSConnectedComponentsMPI::SendLocalResult(int start_row, int end_row) { + const auto &output = GetOutput(); + const int lr = end_row - start_row; + std::vector send(static_cast(lr) * static_cast(cols_)); + for (int i = 0; i < lr; ++i) { + for (int j = 0; j < cols_; ++j) { + const size_t idx = (static_cast(i) * static_cast(cols_)) + static_cast(j); + send[idx] = output[start_row + i][j]; + } + } + MPI_Send(send.data(), static_cast(send.size()), MPI_INT, 0, 0, MPI_COMM_WORLD); +} + +void MorozovaSConnectedComponentsMPI::ReceiveFinalResult() { + auto &output = GetOutput(); + std::vector recv(static_cast(rows_) * static_cast(cols_)); + MPI_Recv(recv.data(), static_cast(recv.size()), MPI_INT, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + for (int i = 0; i < rows_; ++i) { + for (int j = 0; j < cols_; ++j) { + const size_t idx = (static_cast(i) * static_cast(cols_)) + static_cast(j); + output[i][j] = recv[idx]; + } + } +} + +bool MorozovaSConnectedComponentsMPI::RunImpl() { + InitMPI(); + + if (rows_ == 0 || cols_ == 0) { + return true; + } + + const auto [start, end] = ComputeRowRange(); + ComputeLocalComponents(start, end, rank_ * kLabelOffset); + + if (rank_ == 0) { + GatherLocalResults(); + MergeBoundaries(); + NormalizeLabels(); + BroadcastResult(); + } else { + SendLocalResult(start, end); + ReceiveFinalResult(); + } + + return true; +} + +bool MorozovaSConnectedComponentsMPI::PostProcessingImpl() { + const auto &output = GetOutput(); + int max_label = 0; + for (const auto &row : output) { + for (int v : row) { + max_label = std::max(max_label, v); + } + } + + auto &output_ref = GetOutput(); + output_ref.push_back({max_label}); + return true; +} + +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/report.md b/tasks/morozova_s_connected_components/report.md new file mode 100644 index 0000000000..79cb13097b --- /dev/null +++ b/tasks/morozova_s_connected_components/report.md @@ -0,0 +1,64 @@ +# Маркировка компонент на бинарном изображении (черные области соответствуют объектам, белые – фону). + +- Student: Морозова Софья Андреевна, group 3823Б1ФИ2 +- Technology: SEQ | MPI +- Variant: 31 + +## 1. Introduction +Нужно находить связные области на чёрно-белых картинках. Это полезно для анализа изображений в медицине или компьютерном зрении. Задача учит работать с графами и параллельными вычислениями. + +## 2. Problem Statement +**Формальная постановка задачи:** Найти все связные компоненты на чёрно-белом изображении. +**Входные данные:** +Матрица из 0 и 1 (0 - фон, 1 - объект). +**Выходные данные:** +Матрица с метками компонент и их количество. +**Ограничения:** +- изображение должно быть корректным. +- все процессы должны получить одинаковый результат. +- нельзя использовать готовые библиотеки для маркировки. + +## 3. Baseline Algorithm (Sequential) +Используем поиск в глубину. Проходим изображение построчно. Видим непомеченную единичку - запускаем DFS через стек. Все связанные пиксели получают одну метку. Переходим к следующей компоненте. + +## 4. Parallelization Scheme +Для MPI делим изображение по строкам. Каждый процесс работает со своей частью. Используем простое разделение данных. + +Этапы работы: +1. Процесс 0 загружает изображение. +2. Распределяет строки между процессами. +3. Каждый процесс метит свои компоненты. +4. Собираем результаты на процессе 0. +5. Объединяем метки. + +Сложность алгоритма зависит от размера изображения и числа компонент. + +## 5. Implementation Details +- `ops_seq.hpp / ops_seq.cpp` — последовательная версия с DFS +- `ops_mpi.hpp / ops_mpi.cpp` — MPI-реализация с распределением строк +- `common.hpp` — общие типы данных + +## 6. Experimental Setup +- Процессор: AMD Ryzen 5 3600 6-Core Processor (6 ядер, 12 потоков) +- Память: 16 ГБ RAM +- ОС: Windows 10 +- Компилятор: g++, режим сборки Release + +## 7. Results and Discussion + +### 7.1 Correctness +Корректность проверена сравнением результатов SEQ и MPI версий. Все объекты правильно помечены, фон остаётся нулями. + +### 7.2 Performance +| Mode | Count | Time, s | Speedup | Efficiency | +|------|-------|---------|---------|------------| +| seq | 1 | 0.045 | 1.00 | N/A | +| mpi | 2 | 0.028 | 1.61 | 80.5% | +| mpi | 4 | 0.016 | 2.81 | 70.3% | +| mpi | 6 | 0.013 | 3.46 | 57.7% | + +## 8. Conclusions +Задача выполнена успешно. MPI даёт ускорение, но не идеальное. Код работает на разных платформах. Можно улучшить балансировку нагрузки. + +## 9. References +1. Материалы курса "Параллельное программирование для кластерных систем", ННГУ им. Н.И. Лобачевского \ No newline at end of file diff --git a/tasks/morozova_s_connected_components/seq/include/ops_seq.hpp b/tasks/morozova_s_connected_components/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..85d076e39f --- /dev/null +++ b/tasks/morozova_s_connected_components/seq/include/ops_seq.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "morozova_s_connected_components/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace morozova_s_connected_components { + +class MorozovaSConnectedComponentsSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit MorozovaSConnectedComponentsSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + void ProcessComponent(int start_i, int start_j, int current_label); + + int rows_{0}; + int cols_{0}; +}; + +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/seq/src/ops_seq.cpp b/tasks/morozova_s_connected_components/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..3a1fd431d7 --- /dev/null +++ b/tasks/morozova_s_connected_components/seq/src/ops_seq.cpp @@ -0,0 +1,97 @@ +#include "morozova_s_connected_components/seq/include/ops_seq.hpp" + +#include +#include +#include +#include +#include +#include + +#include "morozova_s_connected_components/common/include/common.hpp" + +namespace morozova_s_connected_components { +MorozovaSConnectedComponentsSEQ::MorozovaSConnectedComponentsSEQ(const InType &in) : BaseTask() { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +namespace { + +constexpr std::array, 8> kShifts = { + {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}}; + +} // namespace + +bool MorozovaSConnectedComponentsSEQ::ValidationImpl() { + const auto &input = GetInput(); + if (input.empty()) { + return false; + } + const size_t cols = input.front().size(); + if (cols == 0) { + return false; + } + return std::ranges::all_of(input, [cols](const auto &row) { return row.size() == cols; }); +} + +bool MorozovaSConnectedComponentsSEQ::PreProcessingImpl() { + GetOutput().assign(GetInput().size(), std::vector(GetInput().front().size(), 0)); + return true; +} + +void MorozovaSConnectedComponentsSEQ::ProcessComponent(int start_i, int start_j, int current_label) { + const auto &input = GetInput(); + auto &output = GetOutput(); + + output[start_i][start_j] = current_label; + std::queue> q; + q.emplace(start_i, start_j); + + while (!q.empty()) { + const auto [x, y] = q.front(); + q.pop(); + + for (const auto &[dx, dy] : kShifts) { + const int nx = x + dx; + const int ny = y + dy; + + if (nx >= 0 && nx < rows_ && ny >= 0 && ny < cols_ && input[nx][ny] != 0 && output[nx][ny] == 0) { + output[nx][ny] = current_label; + q.emplace(nx, ny); + } + } + } +} + +bool MorozovaSConnectedComponentsSEQ::RunImpl() { + const auto &input = GetInput(); + auto &output = GetOutput(); + + rows_ = static_cast(input.size()); + cols_ = static_cast(input.front().size()); + + int current_label = 1; + + for (int i = 0; i < rows_; ++i) { + for (int j = 0; j < cols_; ++j) { + if (input[i][j] != 0 && output[i][j] == 0) { + ProcessComponent(i, j, current_label); + ++current_label; + } + } + } + + return true; +} + +bool MorozovaSConnectedComponentsSEQ::PostProcessingImpl() { + int max_label = 0; + for (const auto &row : GetOutput()) { + for (const int v : row) { + max_label = std::max(max_label, v); + } + } + return max_label > 0; +} + +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/settings.json b/tasks/morozova_s_connected_components/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/morozova_s_connected_components/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/morozova_s_connected_components/tests/.clang-tidy b/tasks/morozova_s_connected_components/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/morozova_s_connected_components/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/morozova_s_connected_components/tests/functional/main.cpp b/tasks/morozova_s_connected_components/tests/functional/main.cpp new file mode 100644 index 0000000000..2c9105d713 --- /dev/null +++ b/tasks/morozova_s_connected_components/tests/functional/main.cpp @@ -0,0 +1,160 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "morozova_s_connected_components/common/include/common.hpp" +#include "morozova_s_connected_components/mpi/include/ops_mpi.hpp" +#include "morozova_s_connected_components/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace morozova_s_connected_components { + +class MorozovaSRunFuncTestsConnectedComponents : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + return std::to_string(std::get<0>(test_param)) + "_" + std::get<1>(test_param); + } + + protected: + void SetUp() override { + auto test_params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + int size = std::get<0>(test_params); + std::string pattern = std::get<1>(test_params); + if (size <= 0) { + input_data_ = {}; + return; + } + input_data_ = std::vector>(size, std::vector(size, 0)); + if (pattern == "cross") { + for (int i = 0; i < size; ++i) { + input_data_[i][size / 2] = 1; + input_data_[size / 2][i] = 1; + } + } else if (pattern == "square") { + for (int i = size / 4; i < 3 * size / 4; ++i) { + for (int j = size / 4; j < 3 * size / 4; ++j) { + input_data_[i][j] = 1; + } + } + } else if (pattern == "dots") { + for (int i = 1; i < size; i += 2) { + for (int j = 1; j < size; j += 2) { + input_data_[i][j] = 1; + } + } + } else if (pattern == "border") { + for (int i = 0; i < size; ++i) { + input_data_[0][i] = 1; + input_data_[size - 1][i] = 1; + input_data_[i][0] = 1; + input_data_[i][size - 1] = 1; + } + } else { + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + if ((i + j) % 2 == 0) { + input_data_[i][j] = 1; + } + } + } + } + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.empty()) { + return true; + } + int reported_components = 0; + if (!output_data.empty() && output_data.back().size() == 1) { + reported_components = output_data.back()[0]; + output_data.pop_back(); + } + if (output_data.size() != input_data_.size()) { + return false; + } + for (std::size_t i = 0; i < input_data_.size(); ++i) { + if (output_data[i].size() != input_data_[i].size()) { + return false; + } + for (std::size_t j = 0; j < input_data_[i].size(); ++j) { + if (input_data_[i][j] == 1) { + if (output_data[i][j] <= 0) { + return false; + } + } else { + if (output_data[i][j] != 0) { + return false; + } + } + } + } + + std::vector labels; + for (const auto &row : output_data) { + for (int label : row) { + if (label > 0) { + labels.push_back(label); + } + } + } + std::ranges::sort(labels); + const auto [first, last] = std::ranges::unique(labels); + labels.erase(first, last); + + if (!labels.empty()) { + if (labels[0] != 1) { + return false; + } + for (std::size_t i = 1; i < labels.size(); ++i) { + if (labels[i] != labels[i - 1] + 1) { + return false; + } + } + if (labels.size() != static_cast(reported_components)) { + return false; + } + } else if (reported_components != 0) { + return false; + } + + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_; +}; + +namespace { + +TEST_P(MorozovaSRunFuncTestsConnectedComponents, ConnectedComponentsTest) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = {std::make_tuple(8, "cross"), std::make_tuple(10, "square"), + std::make_tuple(12, "dots"), std::make_tuple(6, "border")}; + +const auto kTestTasksList = std::tuple_cat(ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_morozova_s_connected_components), + ppc::util::AddFuncTask( + kTestParam, PPC_SETTINGS_morozova_s_connected_components)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); +const auto kPerfTestName = + MorozovaSRunFuncTestsConnectedComponents::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(ConnectedComponentsTests, MorozovaSRunFuncTestsConnectedComponents, kGtestValues, + kPerfTestName); + +} // namespace + +} // namespace morozova_s_connected_components diff --git a/tasks/morozova_s_connected_components/tests/performance/main.cpp b/tasks/morozova_s_connected_components/tests/performance/main.cpp new file mode 100644 index 0000000000..1d190541a9 --- /dev/null +++ b/tasks/morozova_s_connected_components/tests/performance/main.cpp @@ -0,0 +1,91 @@ +#include + +#include +#include + +#include "morozova_s_connected_components/common/include/common.hpp" +#include "morozova_s_connected_components/mpi/include/ops_mpi.hpp" +#include "morozova_s_connected_components/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace morozova_s_connected_components { + +class MorozovaSRunPerfTestConnectedComponents : public ppc::util::BaseRunPerfTests { + const int kImageSize_ = 100; + InType input_data_; + + void SetUp() override { + input_data_ = std::vector>(kImageSize_, std::vector(kImageSize_, 0)); + for (int i = 10; i < 40; ++i) { + input_data_[i][20] = 1; + input_data_[i][21] = 1; + } + for (int j = 60; j < 90; ++j) { + input_data_[50][j] = 1; + input_data_[51][j] = 1; + } + for (int i = 70; i < 85; ++i) { + for (int j = 70; j < 85; ++j) { + input_data_[i][j] = 1; + } + } + for (int i = 0; i < 20; ++i) { + input_data_[80 + i][10 + i] = 1; + } + input_data_[5][5] = 1; + input_data_[95][95] = 1; + input_data_[10][90] = 1; + input_data_[90][10] = 1; + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.empty()) { + return false; + } + if (!output_data.empty() && output_data.back().size() == 1) { + output_data.pop_back(); + } + if (output_data.empty()) { + return false; + } + int object_count = 0; + int labeled_count = 0; + for (std::size_t i = 0; i < input_data_.size(); ++i) { + if (i >= output_data.size()) { + return false; + } + for (std::size_t j = 0; j < input_data_[i].size(); ++j) { + if (j >= output_data[i].size()) { + return false; + } + if (input_data_[i][j] == 1) { + object_count++; + if (output_data[i][j] > 0) { + labeled_count++; + } + } else { + if (output_data[i][j] != 0) { + return false; + } + } + } + } + return object_count == labeled_count; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(MorozovaSRunPerfTestConnectedComponents, RunPerfModes) { + ExecuteTest(GetParam()); +} +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks( + PPC_SETTINGS_morozova_s_connected_components); +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); +const auto kPerfTestName = MorozovaSRunPerfTestConnectedComponents::CustomPerfTestName; +INSTANTIATE_TEST_SUITE_P(RunModeTests, MorozovaSRunPerfTestConnectedComponents, kGtestValues, kPerfTestName); + +} // namespace morozova_s_connected_components