From b829349569fddb6ed2fad20a87de51a2f228f362 Mon Sep 17 00:00:00 2001 From: hydroimperium Date: Sat, 28 Feb 2026 19:26:44 +0000 Subject: [PATCH 1/5] Add second task: torus topology (MPI/SEQ), variant 9 --- .../klimov_m_torus/common/include/common.hpp | 26 +++ tasks/klimov_m_torus/info.json | 9 + tasks/klimov_m_torus/mpi/include/ops_mpi.hpp | 47 ++++ tasks/klimov_m_torus/mpi/src/ops_mpi.cpp | 212 ++++++++++++++++++ tasks/klimov_m_torus/report.md | 75 +++++++ tasks/klimov_m_torus/seq/include/ops_seq.hpp | 25 +++ tasks/klimov_m_torus/seq/src/ops_seq.cpp | 43 ++++ tasks/klimov_m_torus/settings.json | 7 + .../klimov_m_torus/tests/functional/main.cpp | 109 +++++++++ 9 files changed, 553 insertions(+) create mode 100644 tasks/klimov_m_torus/common/include/common.hpp create mode 100644 tasks/klimov_m_torus/info.json create mode 100644 tasks/klimov_m_torus/mpi/include/ops_mpi.hpp create mode 100644 tasks/klimov_m_torus/mpi/src/ops_mpi.cpp create mode 100644 tasks/klimov_m_torus/report.md create mode 100644 tasks/klimov_m_torus/seq/include/ops_seq.hpp create mode 100644 tasks/klimov_m_torus/seq/src/ops_seq.cpp create mode 100644 tasks/klimov_m_torus/settings.json create mode 100644 tasks/klimov_m_torus/tests/functional/main.cpp diff --git a/tasks/klimov_m_torus/common/include/common.hpp b/tasks/klimov_m_torus/common/include/common.hpp new file mode 100644 index 00000000..500a945d --- /dev/null +++ b/tasks/klimov_m_torus/common/include/common.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "task/include/task.hpp" + +namespace klimov_m_torus { + +struct TransferRequest { + int source; + int dest; + std::vector payload; +}; + +struct TransferResult { + std::vector payload; + std::vector path; +}; + +using InType = TransferRequest; +using OutType = TransferResult; +using TestParam = std::tuple; +using BaseTask = ppc::task::Task; + +} // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/info.json b/tasks/klimov_m_torus/info.json new file mode 100644 index 00000000..150dc390 --- /dev/null +++ b/tasks/klimov_m_torus/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Михаил", + "group_number": "3823Б1ПР2", + "last_name": "Климов", + "middle_name": "Дмитриевич", + "task_number": "2" + } +} diff --git a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp new file mode 100644 index 00000000..fc520198 --- /dev/null +++ b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include "klimov_m_torus/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace klimov_m_torus { + +class TorusNetworkMpi : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + + explicit TorusNetworkMpi(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static std::pair CalculateGridDimensions(int totalProcs); + static int RankFromCoordinates(int row, int col, int rows, int cols); + static std::pair CoordinatesFromRank(int rank, int cols); + static std::vector BuildRoute(int rows, int cols, int from, int to); + + void BroadcastSourceAndDestination(int &src, int &dst); + void BroadcastDataSize(int src, int &dataSize) const; + std::vector PrepareDataBuffer(int src, int dataSize) const; + void ForwardData(int src, int dst, const std::vector &route, + const std::vector &buffer, std::vector &received) const; + void SaveResult(int dst, const std::vector &received, const std::vector &route); + + InType localInput_{}; + OutType localOutput_{}; + + int currentRank_{0}; + int worldSize_{0}; + + int gridRows_{1}; + int gridCols_{1}; +}; + +} // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp new file mode 100644 index 00000000..1d907313 --- /dev/null +++ b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp @@ -0,0 +1,212 @@ +#include "klimov_m_torus/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "klimov_m_torus/common/include/common.hpp" + +namespace klimov_m_torus { + +TorusNetworkMpi::TorusNetworkMpi(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +std::pair TorusNetworkMpi::CalculateGridDimensions(int totalProcs) { + int rows = static_cast(std::sqrt(static_cast(totalProcs))); + while (rows > 1 && (totalProcs % rows != 0)) { + --rows; + } + if (rows <= 0) rows = 1; + int cols = totalProcs / rows; + if (cols <= 0) cols = 1; + return {rows, cols}; +} + +int TorusNetworkMpi::RankFromCoordinates(int row, int col, int rows, int cols) { + int wrappedRow = ((row % rows) + rows) % rows; + int wrappedCol = ((col % cols) + cols) % cols; + return (wrappedRow * cols) + wrappedCol; +} + +std::pair TorusNetworkMpi::CoordinatesFromRank(int rank, int cols) { + int r = rank / cols; + int c = rank % cols; + return {r, c}; +} + +std::vector TorusNetworkMpi::BuildRoute(int rows, int cols, int from, int to) { + std::vector route; + if (rows <= 0 || cols <= 0) { + route.push_back(from); + return route; + } + + auto [srcRow, srcCol] = CoordinatesFromRank(from, cols); + auto [dstRow, dstCol] = CoordinatesFromRank(to, cols); + + int curRow = srcRow; + int curCol = srcCol; + route.push_back(from); + + int colDiff = dstCol - srcCol; + int rightSteps = (colDiff >= 0) ? colDiff : colDiff + cols; + int leftSteps = (colDiff <= 0) ? -colDiff : cols - colDiff; + int stepCol = (rightSteps <= leftSteps) ? 1 : -1; + int stepsCol = (rightSteps <= leftSteps) ? rightSteps : leftSteps; + + for (int i = 0; i < stepsCol; ++i) { + curCol += stepCol; + route.push_back(RankFromCoordinates(curRow, curCol, rows, cols)); + } + + int rowDiff = dstRow - srcRow; + int downSteps = (rowDiff >= 0) ? rowDiff : rowDiff + rows; + int upSteps = (rowDiff <= 0) ? -rowDiff : rows - rowDiff; + int stepRow = (downSteps <= upSteps) ? 1 : -1; + int stepsRow = (downSteps <= upSteps) ? downSteps : upSteps; + + for (int i = 0; i < stepsRow; ++i) { + curRow += stepRow; + route.push_back(RankFromCoordinates(curRow, curCol, rows, cols)); + } + + return route; +} + +bool TorusNetworkMpi::ValidationImpl() { + int initialized = 0; + MPI_Initialized(&initialized); + if (initialized == 0) return false; + + MPI_Comm_rank(MPI_COMM_WORLD, ¤tRank_); + MPI_Comm_size(MPI_COMM_WORLD, &worldSize_); + + int valid = 0; + if (currentRank_ == 0) { + const auto &req = GetInput(); + if (req.source >= 0 && req.dest >= 0 && + req.source < worldSize_ && req.dest < worldSize_) { + valid = 1; + } + } + MPI_Bcast(&valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + return valid != 0; +} + +bool TorusNetworkMpi::PreProcessingImpl() { + MPI_Comm_rank(MPI_COMM_WORLD, ¤tRank_); + MPI_Comm_size(MPI_COMM_WORLD, &worldSize_); + + auto [r, c] = CalculateGridDimensions(worldSize_); + gridRows_ = r; + gridCols_ = c; + + localInput_ = GetInput(); + localOutput_ = OutType{}; + return true; +} + +bool TorusNetworkMpi::RunImpl() { + int sender = 0, receiver = 0; + BroadcastSourceAndDestination(sender, receiver); + + int dataSize = 0; + BroadcastDataSize(sender, dataSize); + + std::vector dataBuffer = PrepareDataBuffer(sender, dataSize); + std::vector transmissionRoute = BuildRoute(gridRows_, gridCols_, sender, receiver); + + std::vector receivedData; + ForwardData(sender, receiver, transmissionRoute, dataBuffer, receivedData); + + SaveResult(receiver, receivedData, transmissionRoute); + return true; +} + +void TorusNetworkMpi::BroadcastSourceAndDestination(int &src, int &dst) { + if (currentRank_ == 0) { + const auto &req = GetInput(); + src = req.source; + dst = req.dest; + } + MPI_Bcast(&src, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&dst, 1, MPI_INT, 0, MPI_COMM_WORLD); +} + +void TorusNetworkMpi::BroadcastDataSize(int src, int &dataSize) const { + if (currentRank_ == src) { + dataSize = static_cast(localInput_.payload.size()); + } + MPI_Bcast(&dataSize, 1, MPI_INT, src, MPI_COMM_WORLD); +} + +std::vector TorusNetworkMpi::PrepareDataBuffer(int src, int dataSize) const { + std::vector buffer(dataSize); + if (currentRank_ == src && dataSize > 0) { + std::copy(localInput_.payload.begin(), localInput_.payload.end(), buffer.begin()); + } + return buffer; +} + +void TorusNetworkMpi::ForwardData(int src, int dst, const std::vector &route, + const std::vector &buffer, std::vector &received) const { + const int routeLen = static_cast(route.size()); + auto it = std::find(route.begin(), route.end(), currentRank_); + bool onRoute = (it != route.end()); + int myIndex = onRoute ? static_cast(std::distance(route.begin(), it)) : -1; + + if (src == dst) { + if (currentRank_ == src) { + received = buffer; + } + } else if (currentRank_ == src) { + received = buffer; + if (routeLen > 1) { + int nextHop = route[1]; + int sendSize = static_cast(buffer.size()); + MPI_Send(&sendSize, 1, MPI_INT, nextHop, 0, MPI_COMM_WORLD); + if (sendSize > 0) { + MPI_Send(received.data(), sendSize, MPI_INT, nextHop, 1, MPI_COMM_WORLD); + } + } + } else if (onRoute) { + int prevHop = route[myIndex - 1]; + int recvSize = 0; + MPI_Recv(&recvSize, 1, MPI_INT, prevHop, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + received.resize(recvSize); + if (recvSize > 0) { + MPI_Recv(received.data(), recvSize, MPI_INT, prevHop, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } + + if (currentRank_ != dst && myIndex + 1 < routeLen) { + int nextHop = route[myIndex + 1]; + MPI_Send(&recvSize, 1, MPI_INT, nextHop, 0, MPI_COMM_WORLD); + if (recvSize > 0) { + MPI_Send(received.data(), recvSize, MPI_INT, nextHop, 1, MPI_COMM_WORLD); + } + } + } +} + +void TorusNetworkMpi::SaveResult(int dst, const std::vector &received, const std::vector &route) { + if (currentRank_ == dst) { + localOutput_.payload = received; + localOutput_.path = route; + GetOutput() = localOutput_; + } else { + GetOutput() = OutType{}; + } +} + +bool TorusNetworkMpi::PostProcessingImpl() { + return true; +} + +} // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/report.md b/tasks/klimov_m_torus/report.md new file mode 100644 index 00000000..265048b4 --- /dev/null +++ b/tasks/klimov_m_torus/report.md @@ -0,0 +1,75 @@ +# Решетка-тор (Mesh-Torus) + +- **Студент**: Климов Михаил Дмитриевич, группа 3823Б1ПР2 +- **Технология**: MPI / SEQ +- **Вариант**: 9 + +## 1. Введение +Цель работы – реализовать виртуальную топологию «решётка-тор» средствами MPI, используя только точечные коммуникации (`MPI_Send`/`MPI_Recv`) без применения готовых функций создания топологий (`MPI_Cart_create`). Задача заключается в организации передачи данных от произвольного процесса-отправителя к произвольному процессу-получателю в рамках двумерной тороидальной решётки, а также в построении маршрута передачи. + +## 2. Постановка задачи +На вход подаются три параметра: номер исходного процесса `source`, номер целевого процесса `dest` и вектор целочисленных данных `payload`. Требуется передать данные от `source` к `dest` по кратчайшему пути в топологии «решётка-тор» и зафиксировать пройденный маршрут (последовательность номеров процессов). Размеры решётки определяются автоматически исходя из общего числа MPI-процессов (квадратная или прямоугольная сетка, максимально близкая к квадрату). Выходные данные – полученный вектор `payload` и вектор `path`, содержащий все промежуточные процессы (включая источник и цель). + +## 3. Описание последовательного алгоритма +Последовательная версия реализована в классе `TorusSequential`. Поскольку в последовательном случае передача данных не требуется, алгоритм тривиален: +1. Проверка корректности входных данных (`source` и `dest` неотрицательны). +2. Копирование входного вектора `payload` в выходной. +3. Формирование пути: если `source == dest`, путь состоит из одного элемента; иначе – из двух элементов: `source` и `dest`. + +Данная реализация служит эталоном для проверки корректности MPI-версии на малом числе процессов. + +## 4. Схема распараллеливания (MPI) +Параллельная версия реализована в классе `TorusNetworkMpi`. Основные этапы: + +1. **Определение размеров решётки**. На этапе предварительной обработки все процессы вычисляют размеры сетки `gridRows_` и `gridCols_` на основе общего числа процессов `worldSize_`. Используется метод `CalculateGridDimensions`, который подбирает такие `rows` и `cols`, чтобы их произведение равнялось `worldSize_`, а сетка была как можно более квадратной. + +2. **Преобразование «ранг ↔ координаты»**. Реализованы функции `RankFromCoordinates` и `CoordinatesFromRank`, позволяющие переходить от двумерных координат `(row, col)` к рангу MPI и обратно. Учёт тороидальности обеспечивается взятием по модулю. + +3. **Построение кратчайшего пути**. Метод `BuildRoute` строит маршрут от `source` до `dest` в тороидальной сетке. Сначала происходит движение по столбцам (выбирается кратчайшее направление – вправо или влево с учётом зацикливания), затем – по строкам. Полученный путь – это последовательность рангов, через которые пройдёт сообщение. + +4. **Распространение данных**. Процесс с рангом 0 рассылает всем номера `source` и `dest`. Далее процесс-отправитель (`source`) широковещательно передаёт размер своего вектора `payload` (через `MPI_Bcast`), а затем все процессы, участвующие в пути, организуют цепочку передачи: + - Отправитель сохраняет свои данные в буфере и отправляет их следующему процессу в пути (если он не является получателем). + - Каждый промежуточный процесс принимает данные от предыдущего, сохраняет их у себя и, если он не является конечным получателем, отправляет дальше. + - Получатель принимает данные и сохраняет их в выходной структуре, также фиксируя полный путь. + +Взаимодействие между соседями по пути осуществляется только с помощью `MPI_Send` и `MPI_Recv`, что строго соответствует условию задачи (без использования коллективных операций для пересылки данных). + +## 5. Детали реализации +- Класс `TorusNetworkMpi` содержит вспомогательные методы: `CalculateGridDimensions`, `RankFromCoordinates`, `CoordinatesFromRank`, `BuildRoute`, `BroadcastSourceAndDestination`, `BroadcastDataSize`, `PrepareDataBuffer`, `ForwardData`, `SaveResult`. +- Все обмены данными между процессами выполняются по принципу «каждый передаёт только следующему»; гарантируется, что сообщение не зациклится и дойдёт до адресата. +- Валидация входных данных включает проверку на то, что `source` и `dest` находятся в допустимых пределах и что MPI инициализирован. +- Последовательная версия `TorusSequential` максимально упрощена и служит только для функционального тестирования. +- Тесты параметризованы размером данных (`1, 4, 8, 16, 32` элемента) и проверяют, что: + - Данные доходят до получателя без искажений. + - Путь содержит корректные начальную и конечную точки. + - Промежуточные процессы (кроме получателя) не сохраняют результат. + +## 6. Экспериментальная установка +- **Аппаратное обеспечение**: виртуальная среда (контейнер Docker) с выделением 4 ядер и 8 ГБ ОЗУ. +- **ОС**: Ubuntu 24.04 LTS (внутри контейнера). +- **Компилятор**: g++-14. +- **Сборка**: CMake с типом `RelWithDebInfo`, санитайзеры не использовались при финальных тестах. +- **Среда выполнения**: MPI-тесты запускались с `PPC_NUM_PROC=2`, `4`, `8` (для проверки при разных размерах сетки) через скрипт `scripts/run_tests.py`. Количество процессов подбиралось так, чтобы можно было построить прямоугольную сетку (например, 2→1x2, 4→2x2, 8→2x4 или 4x2). + +## 7. Результаты и обсуждение + +### 7.1 Корректность +Функциональные тесты покрывают следующие сценарии: +- Передача данных от процесса 0 к процессу `world_size-1` (наиболее удалённому). +- Различные размеры полезной нагрузки (от 1 до 32 целых чисел). +- Проверка, что только процесс-получатель сохраняет данные, а остальные (включая промежуточные) возвращают пустой результат. +- Проверка, что путь начинается с `source` и заканчивается `dest`. + +Все тесты успешно проходятся как для последовательной, так и для MPI-версии при любом допустимом количестве процессов. Это подтверждает правильность построения пути и передачи данных в тороидальной топологии. + +### 7.2 Особенности реализации +- Использованный метод построения пути (сначала по столбцам, затем по строкам) всегда даёт кратчайший маршрут в метрике Манхэттена на торе, так как движения по разным осям независимы. +- Благодаря модульным вычислениям координат (`(x % N + N) % N`) обеспечивается корректная работа на тороидальных границах. +- При `source == dest` данные не передаются по сети, и путь состоит из одного процесса. + +## 8. Выводы +Разработана MPI-программа, реализующая передачу данных в топологии «решётка-тор» с использованием только `MPI_Send/Recv`. Программа автоматически определяет размеры сетки, строит кратчайший путь и выполняет доставку сообщения по цепочке процессов. Функциональное тестирование подтверждает корректность работы для различных конфигураций процессов и размеров данных. Реализация полностью соответствует условиям задачи и может служить основой для дальнейших экспериментов с маршрутизацией в тороидальных сетях. + +## 9. Источники +1. Microsoft MPI : документация // Microsoft Learn. – URL: https://learn.microsoft.com/ru-ru/message-passing-interface/microsoft-mpi +2. Сысоев А. В. Курс лекций по параллельному программированию \ No newline at end of file diff --git a/tasks/klimov_m_torus/seq/include/ops_seq.hpp b/tasks/klimov_m_torus/seq/include/ops_seq.hpp new file mode 100644 index 00000000..9b061c04 --- /dev/null +++ b/tasks/klimov_m_torus/seq/include/ops_seq.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "klimov_m_torus/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace klimov_m_torus { + +class TorusSequential : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit TorusSequential(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + InType localInput_{}; + OutType localOutput_{}; +}; + +} // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/seq/src/ops_seq.cpp b/tasks/klimov_m_torus/seq/src/ops_seq.cpp new file mode 100644 index 00000000..f5edccdf --- /dev/null +++ b/tasks/klimov_m_torus/seq/src/ops_seq.cpp @@ -0,0 +1,43 @@ +#include "klimov_m_torus/seq/include/ops_seq.hpp" + +#include + +#include "klimov_m_torus/common/include/common.hpp" + +namespace klimov_m_torus { + +TorusSequential::TorusSequential(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = {}; +} + +bool TorusSequential::ValidationImpl() { + const auto &req = GetInput(); + return req.source >= 0 && req.dest >= 0; +} + +bool TorusSequential::PreProcessingImpl() { + return true; +} + +bool TorusSequential::RunImpl() { + const auto &req = GetInput(); + auto &out = GetOutput(); + + out.payload = req.payload; + + out.path.clear(); + out.path.push_back(req.source); + if (req.source != req.dest) { + out.path.push_back(req.dest); + } + + return true; +} + +bool TorusSequential::PostProcessingImpl() { + return true; +} + +} // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/settings.json b/tasks/klimov_m_torus/settings.json new file mode 100644 index 00000000..b1a0d525 --- /dev/null +++ b/tasks/klimov_m_torus/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/klimov_m_torus/tests/functional/main.cpp b/tasks/klimov_m_torus/tests/functional/main.cpp new file mode 100644 index 00000000..e3c796c8 --- /dev/null +++ b/tasks/klimov_m_torus/tests/functional/main.cpp @@ -0,0 +1,109 @@ +#include +#include + +#include +#include +#include +#include + +#include "klimov_m_torus/common/include/common.hpp" +#include "klimov_m_torus/mpi/include/ops_mpi.hpp" +#include "klimov_m_torus/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" + +namespace klimov_m_torus { + +class TorusNetworkTest : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestParam &test_param) { + int size = std::get<0>(test_param); + return "size" + std::to_string(size); + } + + protected: + void SetUp() override { + TestParam params = std::get<2>(GetParam()); + int data_size = std::get<0>(params); + + int mpi_initialized = 0; + MPI_Initialized(&mpi_initialized); + if (mpi_initialized != 0) { + MPI_Comm_size(MPI_COMM_WORLD, &world_size_); + MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + } else { + world_size_ = 1; + rank_ = 0; + } + + source_ = 0; + dest_ = (world_size_ > 1) ? (world_size_ - 1) : 0; + + input_.source = source_; + input_.dest = dest_; + input_.payload.clear(); + for (int i = 0; i < data_size; ++i) { + input_.payload.push_back(i + 1); + } + expected_payload_ = input_.payload; + } + + InType GetTestInputData() override { + return input_; + } + + bool CheckTestOutputData(OutType &out) override { + const std::string &task_name = std::get<1>(GetParam()); + const bool is_seq = (task_name.find("seq") != std::string::npos); + + return is_seq ? CheckSequential(out) : CheckParallel(out); + } + + private: + [[nodiscard]] bool CheckSequential(const OutType &out) const { + if (out.payload != expected_payload_) return false; + if (out.path.empty()) return false; + if (out.path.front() != source_) return false; + if (out.path.back() != dest_) return false; + return true; + } + + [[nodiscard]] bool CheckParallel(const OutType &out) const { + if (rank_ != dest_) { + return out.payload.empty() && out.path.empty(); + } + if (out.payload != expected_payload_) return false; + if (out.path.empty()) return false; + if (out.path.front() != source_) return false; + if (out.path.back() != dest_) return false; + return true; + } + + InType input_{}; + std::vector expected_payload_; + int world_size_{1}; + int rank_{0}; + int source_{0}; + int dest_{0}; +}; + +namespace { + +const std::array kTestParams = { + std::make_tuple(1), std::make_tuple(4), std::make_tuple(8), std::make_tuple(16), std::make_tuple(32), +}; + +const auto kTasksList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json"), + ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json")); + +const auto kValues = ppc::util::ExpandToValues(kTasksList); +const auto kNamePrinter = TorusNetworkTest::PrintFuncTestName; + +TEST_P(TorusNetworkTest, TorusDataTransfer) { + ExecuteTest(GetParam()); +} + +INSTANTIATE_TEST_SUITE_P(TorusTests, TorusNetworkTest, kValues, kNamePrinter); + +} // namespace +} // namespace klimov_m_torus \ No newline at end of file From 248d43dfc1412efe691a20daac7dc429f5aac19e Mon Sep 17 00:00:00 2001 From: hydroimperium Date: Sun, 1 Mar 2026 14:35:06 +0000 Subject: [PATCH 2/5] Fix --- .../klimov_m_torus/common/include/common.hpp | 10 +- tasks/klimov_m_torus/mpi/include/ops_mpi.hpp | 36 ++-- tasks/klimov_m_torus/mpi/src/ops_mpi.cpp | 202 +++++++++--------- tasks/klimov_m_torus/report.md | 34 +-- tasks/klimov_m_torus/seq/include/ops_seq.hpp | 8 +- tasks/klimov_m_torus/seq/src/ops_seq.cpp | 22 +- tasks/klimov_m_torus/tests/.clang-tidy | 13 ++ .../klimov_m_torus/tests/functional/main.cpp | 48 ++--- .../klimov_m_torus/tests/performance/main.cpp | 98 +++++++++ 9 files changed, 292 insertions(+), 179 deletions(-) create mode 100644 tasks/klimov_m_torus/tests/.clang-tidy create mode 100644 tasks/klimov_m_torus/tests/performance/main.cpp diff --git a/tasks/klimov_m_torus/common/include/common.hpp b/tasks/klimov_m_torus/common/include/common.hpp index 500a945d..b02036eb 100644 --- a/tasks/klimov_m_torus/common/include/common.hpp +++ b/tasks/klimov_m_torus/common/include/common.hpp @@ -8,14 +8,14 @@ namespace klimov_m_torus { struct TransferRequest { - int source; - int dest; - std::vector payload; + int sender; + int receiver; + std::vector data; }; struct TransferResult { - std::vector payload; - std::vector path; + std::vector received_data; + std::vector route; }; using InType = TransferRequest; diff --git a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp index fc520198..50f5b80b 100644 --- a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp +++ b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp @@ -8,13 +8,13 @@ namespace klimov_m_torus { -class TorusNetworkMpi : public BaseTask { +class TorusMeshCommunicator : public BaseTask { public: static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { return ppc::task::TypeOfTask::kMPI; } - explicit TorusNetworkMpi(const InType &in); + explicit TorusMeshCommunicator(const InType &in); private: bool ValidationImpl() override; @@ -22,26 +22,26 @@ class TorusNetworkMpi : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; - static std::pair CalculateGridDimensions(int totalProcs); - static int RankFromCoordinates(int row, int col, int rows, int cols); - static std::pair CoordinatesFromRank(int rank, int cols); - static std::vector BuildRoute(int rows, int cols, int from, int to); + static std::pair CalculateGridSize(int totalProcesses); + static int CombineCoordinates(int row, int col, int rows, int cols); + static std::pair SplitRank(int rank, int cols); + static std::vector BuildMessageRoute(int rows, int cols, int from, int to); - void BroadcastSourceAndDestination(int &src, int &dst); - void BroadcastDataSize(int src, int &dataSize) const; - std::vector PrepareDataBuffer(int src, int dataSize) const; - void ForwardData(int src, int dst, const std::vector &route, - const std::vector &buffer, std::vector &received) const; - void SaveResult(int dst, const std::vector &received, const std::vector &route); + void DistributeSenderReceiver(int &src, int &dst); + void DistributeDataLength(int src, int &len) const; + std::vector AssembleSendBuffer(int src, int len) const; + void RelayMessage(int src, int dst, const std::vector &route, + const std::vector &buffer, std::vector &output) const; + void SaveFinalResult(int dst, const std::vector &output, const std::vector &route); - InType localInput_{}; - OutType localOutput_{}; + InType local_request_{}; + OutType local_response_{}; - int currentRank_{0}; - int worldSize_{0}; + int current_rank_{0}; + int total_ranks_{0}; - int gridRows_{1}; - int gridCols_{1}; + int grid_rows_{1}; + int grid_cols_{1}; }; } // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp index 1d907313..229e59de 100644 --- a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp +++ b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp @@ -12,87 +12,87 @@ namespace klimov_m_torus { -TorusNetworkMpi::TorusNetworkMpi(const InType &in) { +TorusMeshCommunicator::TorusMeshCommunicator(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; GetOutput() = {}; } -std::pair TorusNetworkMpi::CalculateGridDimensions(int totalProcs) { - int rows = static_cast(std::sqrt(static_cast(totalProcs))); - while (rows > 1 && (totalProcs % rows != 0)) { +std::pair TorusMeshCommunicator::CalculateGridSize(int totalProcesses) { + int rows = static_cast(std::sqrt(static_cast(totalProcesses))); + while (rows > 1 && (totalProcesses % rows != 0)) { --rows; } if (rows <= 0) rows = 1; - int cols = totalProcs / rows; + int cols = totalProcesses / rows; if (cols <= 0) cols = 1; return {rows, cols}; } -int TorusNetworkMpi::RankFromCoordinates(int row, int col, int rows, int cols) { - int wrappedRow = ((row % rows) + rows) % rows; - int wrappedCol = ((col % cols) + cols) % cols; - return (wrappedRow * cols) + wrappedCol; +int TorusMeshCommunicator::CombineCoordinates(int row, int col, int rows, int cols) { + int wrapped_row = ((row % rows) + rows) % rows; + int wrapped_col = ((col % cols) + cols) % cols; + return (wrapped_row * cols) + wrapped_col; } -std::pair TorusNetworkMpi::CoordinatesFromRank(int rank, int cols) { +std::pair TorusMeshCommunicator::SplitRank(int rank, int cols) { int r = rank / cols; int c = rank % cols; return {r, c}; } -std::vector TorusNetworkMpi::BuildRoute(int rows, int cols, int from, int to) { +std::vector TorusMeshCommunicator::BuildMessageRoute(int rows, int cols, int from, int to) { std::vector route; if (rows <= 0 || cols <= 0) { route.push_back(from); return route; } - auto [srcRow, srcCol] = CoordinatesFromRank(from, cols); - auto [dstRow, dstCol] = CoordinatesFromRank(to, cols); + auto [src_row, src_col] = SplitRank(from, cols); + auto [dst_row, dst_col] = SplitRank(to, cols); - int curRow = srcRow; - int curCol = srcCol; + int cur_row = src_row; + int cur_col = src_col; route.push_back(from); - int colDiff = dstCol - srcCol; - int rightSteps = (colDiff >= 0) ? colDiff : colDiff + cols; - int leftSteps = (colDiff <= 0) ? -colDiff : cols - colDiff; - int stepCol = (rightSteps <= leftSteps) ? 1 : -1; - int stepsCol = (rightSteps <= leftSteps) ? rightSteps : leftSteps; + int col_diff = dst_col - src_col; + int right_steps = (col_diff >= 0) ? col_diff : col_diff + cols; + int left_steps = (col_diff <= 0) ? -col_diff : cols - col_diff; + int col_step = (right_steps <= left_steps) ? 1 : -1; + int col_moves = (right_steps <= left_steps) ? right_steps : left_steps; - for (int i = 0; i < stepsCol; ++i) { - curCol += stepCol; - route.push_back(RankFromCoordinates(curRow, curCol, rows, cols)); + for (int i = 0; i < col_moves; ++i) { + cur_col += col_step; + route.push_back(CombineCoordinates(cur_row, cur_col, rows, cols)); } - int rowDiff = dstRow - srcRow; - int downSteps = (rowDiff >= 0) ? rowDiff : rowDiff + rows; - int upSteps = (rowDiff <= 0) ? -rowDiff : rows - rowDiff; - int stepRow = (downSteps <= upSteps) ? 1 : -1; - int stepsRow = (downSteps <= upSteps) ? downSteps : upSteps; + int row_diff = dst_row - src_row; + int down_steps = (row_diff >= 0) ? row_diff : row_diff + rows; + int up_steps = (row_diff <= 0) ? -row_diff : rows - row_diff; + int row_step = (down_steps <= up_steps) ? 1 : -1; + int row_moves = (down_steps <= up_steps) ? down_steps : up_steps; - for (int i = 0; i < stepsRow; ++i) { - curRow += stepRow; - route.push_back(RankFromCoordinates(curRow, curCol, rows, cols)); + for (int i = 0; i < row_moves; ++i) { + cur_row += row_step; + route.push_back(CombineCoordinates(cur_row, cur_col, rows, cols)); } return route; } -bool TorusNetworkMpi::ValidationImpl() { +bool TorusMeshCommunicator::ValidationImpl() { int initialized = 0; MPI_Initialized(&initialized); if (initialized == 0) return false; - MPI_Comm_rank(MPI_COMM_WORLD, ¤tRank_); - MPI_Comm_size(MPI_COMM_WORLD, &worldSize_); + MPI_Comm_rank(MPI_COMM_WORLD, ¤t_rank_); + MPI_Comm_size(MPI_COMM_WORLD, &total_ranks_); int valid = 0; - if (currentRank_ == 0) { + if (current_rank_ == 0) { const auto &req = GetInput(); - if (req.source >= 0 && req.dest >= 0 && - req.source < worldSize_ && req.dest < worldSize_) { + if (req.sender >= 0 && req.receiver >= 0 && + req.sender < total_ranks_ && req.receiver < total_ranks_) { valid = 1; } } @@ -100,112 +100,114 @@ bool TorusNetworkMpi::ValidationImpl() { return valid != 0; } -bool TorusNetworkMpi::PreProcessingImpl() { - MPI_Comm_rank(MPI_COMM_WORLD, ¤tRank_); - MPI_Comm_size(MPI_COMM_WORLD, &worldSize_); +bool TorusMeshCommunicator::PreProcessingImpl() { + MPI_Comm_rank(MPI_COMM_WORLD, ¤t_rank_); + MPI_Comm_size(MPI_COMM_WORLD, &total_ranks_); - auto [r, c] = CalculateGridDimensions(worldSize_); - gridRows_ = r; - gridCols_ = c; + auto [r, c] = CalculateGridSize(total_ranks_); + grid_rows_ = r; + grid_cols_ = c; - localInput_ = GetInput(); - localOutput_ = OutType{}; + local_request_ = GetInput(); + local_response_ = OutType{}; return true; } -bool TorusNetworkMpi::RunImpl() { +bool TorusMeshCommunicator::RunImpl() { int sender = 0, receiver = 0; - BroadcastSourceAndDestination(sender, receiver); + DistributeSenderReceiver(sender, receiver); - int dataSize = 0; - BroadcastDataSize(sender, dataSize); + int data_len = 0; + DistributeDataLength(sender, data_len); - std::vector dataBuffer = PrepareDataBuffer(sender, dataSize); - std::vector transmissionRoute = BuildRoute(gridRows_, gridCols_, sender, receiver); + std::vector send_buffer = AssembleSendBuffer(sender, data_len); + std::vector message_route = BuildMessageRoute(grid_rows_, grid_cols_, sender, receiver); - std::vector receivedData; - ForwardData(sender, receiver, transmissionRoute, dataBuffer, receivedData); + std::vector received_data; + RelayMessage(sender, receiver, message_route, send_buffer, received_data); - SaveResult(receiver, receivedData, transmissionRoute); + SaveFinalResult(receiver, received_data, message_route); return true; } -void TorusNetworkMpi::BroadcastSourceAndDestination(int &src, int &dst) { - if (currentRank_ == 0) { +void TorusMeshCommunicator::DistributeSenderReceiver(int &src, int &dst) { + if (current_rank_ == 0) { const auto &req = GetInput(); - src = req.source; - dst = req.dest; + src = req.sender; + dst = req.receiver; } MPI_Bcast(&src, 1, MPI_INT, 0, MPI_COMM_WORLD); MPI_Bcast(&dst, 1, MPI_INT, 0, MPI_COMM_WORLD); } -void TorusNetworkMpi::BroadcastDataSize(int src, int &dataSize) const { - if (currentRank_ == src) { - dataSize = static_cast(localInput_.payload.size()); +void TorusMeshCommunicator::DistributeDataLength(int src, int &len) const { + if (current_rank_ == src) { + len = static_cast(local_request_.data.size()); } - MPI_Bcast(&dataSize, 1, MPI_INT, src, MPI_COMM_WORLD); + MPI_Bcast(&len, 1, MPI_INT, src, MPI_COMM_WORLD); } -std::vector TorusNetworkMpi::PrepareDataBuffer(int src, int dataSize) const { - std::vector buffer(dataSize); - if (currentRank_ == src && dataSize > 0) { - std::copy(localInput_.payload.begin(), localInput_.payload.end(), buffer.begin()); +std::vector TorusMeshCommunicator::AssembleSendBuffer(int src, int len) const { + std::vector buffer(len); + if (current_rank_ == src && len > 0) { + std::copy(local_request_.data.begin(), local_request_.data.end(), buffer.begin()); } return buffer; } -void TorusNetworkMpi::ForwardData(int src, int dst, const std::vector &route, - const std::vector &buffer, std::vector &received) const { - const int routeLen = static_cast(route.size()); - auto it = std::find(route.begin(), route.end(), currentRank_); - bool onRoute = (it != route.end()); - int myIndex = onRoute ? static_cast(std::distance(route.begin(), it)) : -1; +void TorusMeshCommunicator::RelayMessage(int src, int dst, const std::vector &route, + const std::vector &buffer, + std::vector &output) const { + const int route_len = static_cast(route.size()); + auto it = std::find(route.begin(), route.end(), current_rank_); + bool on_route = (it != route.end()); + int my_pos = on_route ? static_cast(std::distance(route.begin(), it)) : -1; if (src == dst) { - if (currentRank_ == src) { - received = buffer; + if (current_rank_ == src) { + output = buffer; } - } else if (currentRank_ == src) { - received = buffer; - if (routeLen > 1) { - int nextHop = route[1]; - int sendSize = static_cast(buffer.size()); - MPI_Send(&sendSize, 1, MPI_INT, nextHop, 0, MPI_COMM_WORLD); - if (sendSize > 0) { - MPI_Send(received.data(), sendSize, MPI_INT, nextHop, 1, MPI_COMM_WORLD); + } else if (current_rank_ == src) { + output = buffer; + if (route_len > 1) { + int next_hop = route[1]; + int send_len = static_cast(buffer.size()); + MPI_Send(&send_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); + if (send_len > 0) { + MPI_Send(output.data(), send_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); } } - } else if (onRoute) { - int prevHop = route[myIndex - 1]; - int recvSize = 0; - MPI_Recv(&recvSize, 1, MPI_INT, prevHop, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - received.resize(recvSize); - if (recvSize > 0) { - MPI_Recv(received.data(), recvSize, MPI_INT, prevHop, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } else if (on_route) { + int prev_hop = route[my_pos - 1]; + int recv_len = 0; + MPI_Recv(&recv_len, 1, MPI_INT, prev_hop, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + output.resize(recv_len); + if (recv_len > 0) { + MPI_Recv(output.data(), recv_len, MPI_INT, prev_hop, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); } - if (currentRank_ != dst && myIndex + 1 < routeLen) { - int nextHop = route[myIndex + 1]; - MPI_Send(&recvSize, 1, MPI_INT, nextHop, 0, MPI_COMM_WORLD); - if (recvSize > 0) { - MPI_Send(received.data(), recvSize, MPI_INT, nextHop, 1, MPI_COMM_WORLD); + if (current_rank_ != dst && my_pos + 1 < route_len) { + int next_hop = route[my_pos + 1]; + MPI_Send(&recv_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); + if (recv_len > 0) { + MPI_Send(output.data(), recv_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); } } } } -void TorusNetworkMpi::SaveResult(int dst, const std::vector &received, const std::vector &route) { - if (currentRank_ == dst) { - localOutput_.payload = received; - localOutput_.path = route; - GetOutput() = localOutput_; +void TorusMeshCommunicator::SaveFinalResult(int dst, const std::vector &output, + const std::vector &route) { + if (current_rank_ == dst) { + local_response_.received_data = output; + local_response_.route = route; + GetOutput() = local_response_; } else { GetOutput() = OutType{}; } } -bool TorusNetworkMpi::PostProcessingImpl() { +bool TorusMeshCommunicator::PostProcessingImpl() { return true; } diff --git a/tasks/klimov_m_torus/report.md b/tasks/klimov_m_torus/report.md index 265048b4..237116dd 100644 --- a/tasks/klimov_m_torus/report.md +++ b/tasks/klimov_m_torus/report.md @@ -8,26 +8,26 @@ Цель работы – реализовать виртуальную топологию «решётка-тор» средствами MPI, используя только точечные коммуникации (`MPI_Send`/`MPI_Recv`) без применения готовых функций создания топологий (`MPI_Cart_create`). Задача заключается в организации передачи данных от произвольного процесса-отправителя к произвольному процессу-получателю в рамках двумерной тороидальной решётки, а также в построении маршрута передачи. ## 2. Постановка задачи -На вход подаются три параметра: номер исходного процесса `source`, номер целевого процесса `dest` и вектор целочисленных данных `payload`. Требуется передать данные от `source` к `dest` по кратчайшему пути в топологии «решётка-тор» и зафиксировать пройденный маршрут (последовательность номеров процессов). Размеры решётки определяются автоматически исходя из общего числа MPI-процессов (квадратная или прямоугольная сетка, максимально близкая к квадрату). Выходные данные – полученный вектор `payload` и вектор `path`, содержащий все промежуточные процессы (включая источник и цель). +На вход подаются три параметра: номер исходного процесса `sender`, номер целевого процесса `receiver` и вектор целочисленных данных `data`. Требуется передать данные от `sender` к `receiver` по кратчайшему пути в топологии «решётка-тор» и зафиксировать пройденный маршрут (последовательность номеров процессов). Размеры решётки определяются автоматически исходя из общего числа MPI-процессов (квадратная или прямоугольная сетка, максимально близкая к квадрату). Выходные данные – полученный вектор `received_data` и вектор `route`, содержащий все промежуточные процессы (включая источник и цель). ## 3. Описание последовательного алгоритма -Последовательная версия реализована в классе `TorusSequential`. Поскольку в последовательном случае передача данных не требуется, алгоритм тривиален: -1. Проверка корректности входных данных (`source` и `dest` неотрицательны). -2. Копирование входного вектора `payload` в выходной. -3. Формирование пути: если `source == dest`, путь состоит из одного элемента; иначе – из двух элементов: `source` и `dest`. +Последовательная версия реализована в классе `TorusReferenceImpl`. Поскольку в последовательном случае передача данных не требуется, алгоритм тривиален: +1. Проверка корректности входных данных (`sender` и `receiver` неотрицательны). +2. Копирование входного вектора `data` в выходной `received_data`. +3. Формирование маршрута `route`: если `sender == receiver`, маршрут состоит из одного элемента; иначе – из двух элементов: `sender` и `receiver`. Данная реализация служит эталоном для проверки корректности MPI-версии на малом числе процессов. ## 4. Схема распараллеливания (MPI) -Параллельная версия реализована в классе `TorusNetworkMpi`. Основные этапы: +Параллельная версия реализована в классе `TorusMeshCommunicator`. Основные этапы: -1. **Определение размеров решётки**. На этапе предварительной обработки все процессы вычисляют размеры сетки `gridRows_` и `gridCols_` на основе общего числа процессов `worldSize_`. Используется метод `CalculateGridDimensions`, который подбирает такие `rows` и `cols`, чтобы их произведение равнялось `worldSize_`, а сетка была как можно более квадратной. +1. **Определение размеров решётки**. На этапе предварительной обработки все процессы вычисляют размеры сетки `grid_rows_` и `grid_cols_` на основе общего числа процессов `total_ranks_`. Используется метод `CalculateGridSize`, который подбирает такие `rows` и `cols`, чтобы их произведение равнялось `total_ranks_`, а сетка была как можно более квадратной. -2. **Преобразование «ранг ↔ координаты»**. Реализованы функции `RankFromCoordinates` и `CoordinatesFromRank`, позволяющие переходить от двумерных координат `(row, col)` к рангу MPI и обратно. Учёт тороидальности обеспечивается взятием по модулю. +2. **Преобразование «ранг ↔ координаты»**. Реализованы функции `CombineCoordinates` и `SplitRank`, позволяющие переходить от двумерных координат `(row, col)` к рангу MPI и обратно. Учёт тороидальности обеспечивается взятием по модулю. -3. **Построение кратчайшего пути**. Метод `BuildRoute` строит маршрут от `source` до `dest` в тороидальной сетке. Сначала происходит движение по столбцам (выбирается кратчайшее направление – вправо или влево с учётом зацикливания), затем – по строкам. Полученный путь – это последовательность рангов, через которые пройдёт сообщение. +3. **Построение кратчайшего пути**. Метод `BuildMessageRoute` строит маршрут от `sender` до `receiver` в тороидальной сетке. Сначала происходит движение по столбцам (выбирается кратчайшее направление – вправо или влево с учётом зацикливания), затем – по строкам. Полученный путь – это последовательность рангов, через которые пройдёт сообщение. -4. **Распространение данных**. Процесс с рангом 0 рассылает всем номера `source` и `dest`. Далее процесс-отправитель (`source`) широковещательно передаёт размер своего вектора `payload` (через `MPI_Bcast`), а затем все процессы, участвующие в пути, организуют цепочку передачи: +4. **Распространение данных**. Процесс с рангом 0 рассылает всем номера `sender` и `receiver`. Далее процесс-отправитель (`sender`) широковещательно передаёт размер своего вектора `data` (через `MPI_Bcast`), а затем все процессы, участвующие в пути, организуют цепочку передачи: - Отправитель сохраняет свои данные в буфере и отправляет их следующему процессу в пути (если он не является получателем). - Каждый промежуточный процесс принимает данные от предыдущего, сохраняет их у себя и, если он не является конечным получателем, отправляет дальше. - Получатель принимает данные и сохраняет их в выходной структуре, также фиксируя полный путь. @@ -35,13 +35,13 @@ Взаимодействие между соседями по пути осуществляется только с помощью `MPI_Send` и `MPI_Recv`, что строго соответствует условию задачи (без использования коллективных операций для пересылки данных). ## 5. Детали реализации -- Класс `TorusNetworkMpi` содержит вспомогательные методы: `CalculateGridDimensions`, `RankFromCoordinates`, `CoordinatesFromRank`, `BuildRoute`, `BroadcastSourceAndDestination`, `BroadcastDataSize`, `PrepareDataBuffer`, `ForwardData`, `SaveResult`. +- Класс `TorusMeshCommunicator` содержит вспомогательные методы: `CalculateGridSize`, `CombineCoordinates`, `SplitRank`, `BuildMessageRoute`, `DistributeSenderReceiver`, `DistributeDataLength`, `AssembleSendBuffer`, `RelayMessage`, `SaveFinalResult`. - Все обмены данными между процессами выполняются по принципу «каждый передаёт только следующему»; гарантируется, что сообщение не зациклится и дойдёт до адресата. -- Валидация входных данных включает проверку на то, что `source` и `dest` находятся в допустимых пределах и что MPI инициализирован. -- Последовательная версия `TorusSequential` максимально упрощена и служит только для функционального тестирования. +- Валидация входных данных включает проверку на то, что `sender` и `receiver` находятся в допустимых пределах и что MPI инициализирован. +- Последовательная версия `TorusReferenceImpl` максимально упрощена и служит только для функционального тестирования. - Тесты параметризованы размером данных (`1, 4, 8, 16, 32` элемента) и проверяют, что: - Данные доходят до получателя без искажений. - - Путь содержит корректные начальную и конечную точки. + - Маршрут содержит корректные начальную и конечную точки. - Промежуточные процессы (кроме получателя) не сохраняют результат. ## 6. Экспериментальная установка @@ -55,17 +55,17 @@ ### 7.1 Корректность Функциональные тесты покрывают следующие сценарии: -- Передача данных от процесса 0 к процессу `world_size-1` (наиболее удалённому). +- Передача данных от процесса 0 к процессу `total_ranks_-1` (наиболее удалённому). - Различные размеры полезной нагрузки (от 1 до 32 целых чисел). - Проверка, что только процесс-получатель сохраняет данные, а остальные (включая промежуточные) возвращают пустой результат. -- Проверка, что путь начинается с `source` и заканчивается `dest`. +- Проверка, что маршрут начинается с `sender` и заканчивается `receiver`. Все тесты успешно проходятся как для последовательной, так и для MPI-версии при любом допустимом количестве процессов. Это подтверждает правильность построения пути и передачи данных в тороидальной топологии. ### 7.2 Особенности реализации - Использованный метод построения пути (сначала по столбцам, затем по строкам) всегда даёт кратчайший маршрут в метрике Манхэттена на торе, так как движения по разным осям независимы. - Благодаря модульным вычислениям координат (`(x % N + N) % N`) обеспечивается корректная работа на тороидальных границах. -- При `source == dest` данные не передаются по сети, и путь состоит из одного процесса. +- При `sender == receiver` данные не передаются по сети, и маршрут состоит из одного процесса. ## 8. Выводы Разработана MPI-программа, реализующая передачу данных в топологии «решётка-тор» с использованием только `MPI_Send/Recv`. Программа автоматически определяет размеры сетки, строит кратчайший путь и выполняет доставку сообщения по цепочке процессов. Функциональное тестирование подтверждает корректность работы для различных конфигураций процессов и размеров данных. Реализация полностью соответствует условиям задачи и может служить основой для дальнейших экспериментов с маршрутизацией в тороидальных сетях. diff --git a/tasks/klimov_m_torus/seq/include/ops_seq.hpp b/tasks/klimov_m_torus/seq/include/ops_seq.hpp index 9b061c04..cb461544 100644 --- a/tasks/klimov_m_torus/seq/include/ops_seq.hpp +++ b/tasks/klimov_m_torus/seq/include/ops_seq.hpp @@ -5,12 +5,12 @@ namespace klimov_m_torus { -class TorusSequential : public BaseTask { +class TorusReferenceImpl : public BaseTask { public: static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { return ppc::task::TypeOfTask::kSEQ; } - explicit TorusSequential(const InType &in); + explicit TorusReferenceImpl(const InType &in); private: bool ValidationImpl() override; @@ -18,8 +18,8 @@ class TorusSequential : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; - InType localInput_{}; - OutType localOutput_{}; + InType local_request_{}; + OutType local_response_{}; }; } // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/seq/src/ops_seq.cpp b/tasks/klimov_m_torus/seq/src/ops_seq.cpp index f5edccdf..f7963fc2 100644 --- a/tasks/klimov_m_torus/seq/src/ops_seq.cpp +++ b/tasks/klimov_m_torus/seq/src/ops_seq.cpp @@ -6,37 +6,37 @@ namespace klimov_m_torus { -TorusSequential::TorusSequential(const InType &in) { +TorusReferenceImpl::TorusReferenceImpl(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; GetOutput() = {}; } -bool TorusSequential::ValidationImpl() { +bool TorusReferenceImpl::ValidationImpl() { const auto &req = GetInput(); - return req.source >= 0 && req.dest >= 0; + return req.sender >= 0 && req.receiver >= 0; } -bool TorusSequential::PreProcessingImpl() { +bool TorusReferenceImpl::PreProcessingImpl() { return true; } -bool TorusSequential::RunImpl() { +bool TorusReferenceImpl::RunImpl() { const auto &req = GetInput(); auto &out = GetOutput(); - out.payload = req.payload; + out.received_data = req.data; - out.path.clear(); - out.path.push_back(req.source); - if (req.source != req.dest) { - out.path.push_back(req.dest); + out.route.clear(); + out.route.push_back(req.sender); + if (req.sender != req.receiver) { + out.route.push_back(req.receiver); } return true; } -bool TorusSequential::PostProcessingImpl() { +bool TorusReferenceImpl::PostProcessingImpl() { return true; } diff --git a/tasks/klimov_m_torus/tests/.clang-tidy b/tasks/klimov_m_torus/tests/.clang-tidy new file mode 100644 index 00000000..252ef70a --- /dev/null +++ b/tasks/klimov_m_torus/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 + \ No newline at end of file diff --git a/tasks/klimov_m_torus/tests/functional/main.cpp b/tasks/klimov_m_torus/tests/functional/main.cpp index e3c796c8..109c6362 100644 --- a/tasks/klimov_m_torus/tests/functional/main.cpp +++ b/tasks/klimov_m_torus/tests/functional/main.cpp @@ -13,10 +13,10 @@ namespace klimov_m_torus { -class TorusNetworkTest : public ppc::util::BaseRunFuncTests { +class TorusFunctionalTest : public ppc::util::BaseRunFuncTests { public: - static std::string PrintTestParam(const TestParam &test_param) { - int size = std::get<0>(test_param); + static std::string PrintTestParam(const TestParam ¶m) { + int size = std::get<0>(param); return "size" + std::to_string(size); } @@ -38,17 +38,17 @@ class TorusNetworkTest : public ppc::util::BaseRunFuncTests 1) ? (world_size_ - 1) : 0; - input_.source = source_; - input_.dest = dest_; - input_.payload.clear(); + request_.sender = source_; + request_.receiver = dest_; + request_.data.clear(); for (int i = 0; i < data_size; ++i) { - input_.payload.push_back(i + 1); + request_.data.push_back(i + 1); } - expected_payload_ = input_.payload; + expected_payload_ = request_.data; } InType GetTestInputData() override { - return input_; + return request_; } bool CheckTestOutputData(OutType &out) override { @@ -60,25 +60,25 @@ class TorusNetworkTest : public ppc::util::BaseRunFuncTests expected_payload_; int world_size_{1}; int rank_{0}; @@ -93,17 +93,17 @@ const std::array kTestParams = { }; const auto kTasksList = - std::tuple_cat(ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json"), - ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json")); + std::tuple_cat(ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json"), + ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json")); const auto kValues = ppc::util::ExpandToValues(kTasksList); -const auto kNamePrinter = TorusNetworkTest::PrintFuncTestName; +const auto kNamePrinter = TorusFunctionalTest::PrintFuncTestName; -TEST_P(TorusNetworkTest, TorusDataTransfer) { +TEST_P(TorusFunctionalTest, TorusDataTransfer) { ExecuteTest(GetParam()); } -INSTANTIATE_TEST_SUITE_P(TorusTests, TorusNetworkTest, kValues, kNamePrinter); +INSTANTIATE_TEST_SUITE_P(TorusFunctionalTests, TorusFunctionalTest, kValues, kNamePrinter); } // namespace } // namespace klimov_m_torus \ No newline at end of file diff --git a/tasks/klimov_m_torus/tests/performance/main.cpp b/tasks/klimov_m_torus/tests/performance/main.cpp new file mode 100644 index 00000000..2f721cbe --- /dev/null +++ b/tasks/klimov_m_torus/tests/performance/main.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include + +#include "klimov_m_torus/common/include/common.hpp" +#include "klimov_m_torus/mpi/include/ops_mpi.hpp" +#include "klimov_m_torus/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace klimov_m_torus { + +class TorusPerformanceTest : public ppc::util::BaseRunPerfTests { + protected: + InType test_data_; + bool data_ready_ = false; + int world_size_ = 1; + int rank_ = 0; + bool is_seq_mode_ = false; + + void SetUp() override { + std::string task_name = std::get<1>(GetParam()); + is_seq_mode_ = (task_name.find("seq") != std::string::npos); + + int mpi_initialized = 0; + MPI_Initialized(&mpi_initialized); + if (mpi_initialized != 0) { + MPI_Comm_size(MPI_COMM_WORLD, &world_size_); + MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + } + + PrepareTestData(); + } + + void PrepareTestData() { + if (data_ready_) return; + + const int data_size = 10000000; + + test_data_.sender = 0; + if (is_seq_mode_) { + test_data_.receiver = 0; + } else { + test_data_.receiver = (world_size_ > 1) ? (world_size_ - 1) : 0; + } + + test_data_.data.resize(data_size); + for (int i = 0; i < data_size; ++i) { + test_data_.data[i] = i + 1; + } + + data_ready_ = true; + } + + InType GetTestInputData() override { + return test_data_; + } + + bool CheckTestOutputData(OutType &out) override { + std::string task_name = std::get<1>(GetParam()); + bool is_mpi = (task_name.find("mpi") != std::string::npos); + + if (is_mpi) { + if (rank_ != test_data_.receiver) { + return out.received_data.empty() && out.route.empty(); + } + if (out.received_data.size() != test_data_.data.size()) return false; + if (out.received_data.empty()) return true; + return (out.received_data.front() == test_data_.data.front() && + out.received_data.back() == test_data_.data.back()); + } + + if (rank_ == 0) { + if (out.received_data.size() != test_data_.data.size()) return false; + if (out.received_data.empty()) return true; + return (out.received_data.front() == test_data_.data.front() && + out.received_data.back() == test_data_.data.back()); + } + return true; + } +}; + +namespace { + +const auto kPerfTasksTuples = + ppc::util::MakeAllPerfTasks("tasks/klimov_m_torus/settings.json"); + +const auto kPerfValues = ppc::util::TupleToGTestValues(kPerfTasksTuples); +const auto kPerfNamePrinter = TorusPerformanceTest::CustomPerfTestName; + +TEST_P(TorusPerformanceTest, TorusPerformance) { + ExecuteTest(GetParam()); +} + +INSTANTIATE_TEST_SUITE_P(TorusPerformanceTests, TorusPerformanceTest, kPerfValues, kPerfNamePrinter); + +} // namespace +} // namespace klimov_m_torus \ No newline at end of file From fcc305b6681c10559caf7ac9b0630fc1798523df Mon Sep 17 00:00:00 2001 From: hydroimperium Date: Sun, 1 Mar 2026 14:48:34 +0000 Subject: [PATCH 3/5] Fix pre-commit --- .hadolint.yaml | 4 ++-- tasks/klimov_m_torus/tests/.clang-tidy | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.hadolint.yaml b/.hadolint.yaml index b91b263f..e5cb49a3 100644 --- a/.hadolint.yaml +++ b/.hadolint.yaml @@ -1,3 +1,3 @@ ignored: - - DL3008 # Pin versions in apt-get install - - DL3018 # Pin versions in apk add + - DL3008 # Pin versions in apt-get install + - DL3018 # Pin versions in apk add diff --git a/tasks/klimov_m_torus/tests/.clang-tidy b/tasks/klimov_m_torus/tests/.clang-tidy index 252ef70a..7fe3b6a9 100644 --- a/tasks/klimov_m_torus/tests/.clang-tidy +++ b/tasks/klimov_m_torus/tests/.clang-tidy @@ -10,4 +10,3 @@ Checks: > CheckOptions: - key: readability-function-cognitive-complexity.Threshold value: 50 - \ No newline at end of file From 559181f7e51a302741ff2e20e06ea698ef110e46 Mon Sep 17 00:00:00 2001 From: hydroimperium Date: Mon, 2 Mar 2026 17:28:47 +0000 Subject: [PATCH 4/5] Apply fixes --- .../klimov_m_torus/common/include/common.hpp | 14 +++---- tasks/klimov_m_torus/mpi/include/ops_mpi.hpp | 6 +-- tasks/klimov_m_torus/mpi/src/ops_mpi.cpp | 23 ++++++----- tasks/klimov_m_torus/seq/include/ops_seq.hpp | 2 +- tasks/klimov_m_torus/seq/src/ops_seq.cpp | 2 +- tasks/klimov_m_torus/settings.json | 4 +- .../klimov_m_torus/tests/functional/main.cpp | 40 +++++++++++++------ .../klimov_m_torus/tests/performance/main.cpp | 28 ++++++++----- 8 files changed, 74 insertions(+), 45 deletions(-) diff --git a/tasks/klimov_m_torus/common/include/common.hpp b/tasks/klimov_m_torus/common/include/common.hpp index b02036eb..969775be 100644 --- a/tasks/klimov_m_torus/common/include/common.hpp +++ b/tasks/klimov_m_torus/common/include/common.hpp @@ -8,19 +8,19 @@ namespace klimov_m_torus { struct TransferRequest { - int sender; - int receiver; - std::vector data; + int sender; + int receiver; + std::vector data; }; struct TransferResult { - std::vector received_data; - std::vector route; + std::vector received_data; + std::vector route; }; using InType = TransferRequest; using OutType = TransferResult; -using TestParam = std::tuple; +using TestParam = std::tuple; using BaseTask = ppc::task::Task; -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp index 50f5b80b..ae2e3df5 100644 --- a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp +++ b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp @@ -30,8 +30,8 @@ class TorusMeshCommunicator : public BaseTask { void DistributeSenderReceiver(int &src, int &dst); void DistributeDataLength(int src, int &len) const; std::vector AssembleSendBuffer(int src, int len) const; - void RelayMessage(int src, int dst, const std::vector &route, - const std::vector &buffer, std::vector &output) const; + void RelayMessage(int src, int dst, const std::vector &route, const std::vector &buffer, + std::vector &output) const; void SaveFinalResult(int dst, const std::vector &output, const std::vector &route); InType local_request_{}; @@ -44,4 +44,4 @@ class TorusMeshCommunicator : public BaseTask { int grid_cols_{1}; }; -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp index 229e59de..797ba30a 100644 --- a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp +++ b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp @@ -23,9 +23,13 @@ std::pair TorusMeshCommunicator::CalculateGridSize(int totalProcesses) while (rows > 1 && (totalProcesses % rows != 0)) { --rows; } - if (rows <= 0) rows = 1; + if (rows <= 0) { + rows = 1; + } int cols = totalProcesses / rows; - if (cols <= 0) cols = 1; + if (cols <= 0) { + cols = 1; + } return {rows, cols}; } @@ -83,7 +87,9 @@ std::vector TorusMeshCommunicator::BuildMessageRoute(int rows, int cols, in bool TorusMeshCommunicator::ValidationImpl() { int initialized = 0; MPI_Initialized(&initialized); - if (initialized == 0) return false; + if (initialized == 0) { + return false; + } MPI_Comm_rank(MPI_COMM_WORLD, ¤t_rank_); MPI_Comm_size(MPI_COMM_WORLD, &total_ranks_); @@ -91,8 +97,7 @@ bool TorusMeshCommunicator::ValidationImpl() { int valid = 0; if (current_rank_ == 0) { const auto &req = GetInput(); - if (req.sender >= 0 && req.receiver >= 0 && - req.sender < total_ranks_ && req.receiver < total_ranks_) { + if (req.sender >= 0 && req.receiver >= 0 && req.sender < total_ranks_ && req.receiver < total_ranks_) { valid = 1; } } @@ -156,8 +161,7 @@ std::vector TorusMeshCommunicator::AssembleSendBuffer(int src, int len) con } void TorusMeshCommunicator::RelayMessage(int src, int dst, const std::vector &route, - const std::vector &buffer, - std::vector &output) const { + const std::vector &buffer, std::vector &output) const { const int route_len = static_cast(route.size()); auto it = std::find(route.begin(), route.end(), current_rank_); bool on_route = (it != route.end()); @@ -196,8 +200,7 @@ void TorusMeshCommunicator::RelayMessage(int src, int dst, const std::vector &output, - const std::vector &route) { +void TorusMeshCommunicator::SaveFinalResult(int dst, const std::vector &output, const std::vector &route) { if (current_rank_ == dst) { local_response_.received_data = output; local_response_.route = route; @@ -211,4 +214,4 @@ bool TorusMeshCommunicator::PostProcessingImpl() { return true; } -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/seq/include/ops_seq.hpp b/tasks/klimov_m_torus/seq/include/ops_seq.hpp index cb461544..5d203e3c 100644 --- a/tasks/klimov_m_torus/seq/include/ops_seq.hpp +++ b/tasks/klimov_m_torus/seq/include/ops_seq.hpp @@ -22,4 +22,4 @@ class TorusReferenceImpl : public BaseTask { OutType local_response_{}; }; -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/seq/src/ops_seq.cpp b/tasks/klimov_m_torus/seq/src/ops_seq.cpp index f7963fc2..6f88d5fd 100644 --- a/tasks/klimov_m_torus/seq/src/ops_seq.cpp +++ b/tasks/klimov_m_torus/seq/src/ops_seq.cpp @@ -40,4 +40,4 @@ bool TorusReferenceImpl::PostProcessingImpl() { return true; } -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/settings.json b/tasks/klimov_m_torus/settings.json index b1a0d525..16f25e42 100644 --- a/tasks/klimov_m_torus/settings.json +++ b/tasks/klimov_m_torus/settings.json @@ -1,7 +1,7 @@ { - "tasks_type": "processes", "tasks": { "mpi": "enabled", "seq": "enabled" - } + }, + "tasks_type": "processes" } diff --git a/tasks/klimov_m_torus/tests/functional/main.cpp b/tasks/klimov_m_torus/tests/functional/main.cpp index 109c6362..7881a860 100644 --- a/tasks/klimov_m_torus/tests/functional/main.cpp +++ b/tasks/klimov_m_torus/tests/functional/main.cpp @@ -60,10 +60,18 @@ class TorusFunctionalTest : public ppc::util::BaseRunFuncTests kTestParams = { std::make_tuple(1), std::make_tuple(4), std::make_tuple(8), std::make_tuple(16), std::make_tuple(32), }; -const auto kTasksList = - std::tuple_cat(ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json"), - ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json")); +const auto kTasksList = std::tuple_cat( + ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json"), + ppc::util::AddFuncTask(kTestParams, "tasks/klimov_m_torus/settings.json")); const auto kValues = ppc::util::ExpandToValues(kTasksList); const auto kNamePrinter = TorusFunctionalTest::PrintFuncTestName; @@ -106,4 +122,4 @@ TEST_P(TorusFunctionalTest, TorusDataTransfer) { INSTANTIATE_TEST_SUITE_P(TorusFunctionalTests, TorusFunctionalTest, kValues, kNamePrinter); } // namespace -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus diff --git a/tasks/klimov_m_torus/tests/performance/main.cpp b/tasks/klimov_m_torus/tests/performance/main.cpp index 2f721cbe..fe3d9055 100644 --- a/tasks/klimov_m_torus/tests/performance/main.cpp +++ b/tasks/klimov_m_torus/tests/performance/main.cpp @@ -33,9 +33,11 @@ class TorusPerformanceTest : public ppc::util::BaseRunPerfTests } void PrepareTestData() { - if (data_ready_) return; + if (data_ready_) { + return; + } - const int data_size = 10000000; + const int data_size = 10000000; test_data_.sender = 0; if (is_seq_mode_) { @@ -64,15 +66,23 @@ class TorusPerformanceTest : public ppc::util::BaseRunPerfTests if (rank_ != test_data_.receiver) { return out.received_data.empty() && out.route.empty(); } - if (out.received_data.size() != test_data_.data.size()) return false; - if (out.received_data.empty()) return true; + if (out.received_data.size() != test_data_.data.size()) { + return false; + } + if (out.received_data.empty()) { + return true; + } return (out.received_data.front() == test_data_.data.front() && out.received_data.back() == test_data_.data.back()); } if (rank_ == 0) { - if (out.received_data.size() != test_data_.data.size()) return false; - if (out.received_data.empty()) return true; + if (out.received_data.size() != test_data_.data.size()) { + return false; + } + if (out.received_data.empty()) { + return true; + } return (out.received_data.front() == test_data_.data.front() && out.received_data.back() == test_data_.data.back()); } @@ -82,8 +92,8 @@ class TorusPerformanceTest : public ppc::util::BaseRunPerfTests namespace { -const auto kPerfTasksTuples = - ppc::util::MakeAllPerfTasks("tasks/klimov_m_torus/settings.json"); +const auto kPerfTasksTuples = ppc::util::MakeAllPerfTasks( + "tasks/klimov_m_torus/settings.json"); const auto kPerfValues = ppc::util::TupleToGTestValues(kPerfTasksTuples); const auto kPerfNamePrinter = TorusPerformanceTest::CustomPerfTestName; @@ -95,4 +105,4 @@ TEST_P(TorusPerformanceTest, TorusPerformance) { INSTANTIATE_TEST_SUITE_P(TorusPerformanceTests, TorusPerformanceTest, kPerfValues, kPerfNamePrinter); } // namespace -} // namespace klimov_m_torus \ No newline at end of file +} // namespace klimov_m_torus From 9ce8ba4debcbaab69cf7f23164ccacb1826b7e1f Mon Sep 17 00:00:00 2001 From: hydroimperium Date: Mon, 2 Mar 2026 22:24:11 +0000 Subject: [PATCH 5/5] Apply clang-tidy fixes and formatting --- .../klimov_m_torus/common/include/common.hpp | 4 +- tasks/klimov_m_torus/mpi/include/ops_mpi.hpp | 4 +- tasks/klimov_m_torus/mpi/src/ops_mpi.cpp | 90 +++++++++++-------- tasks/klimov_m_torus/tests/.clang-tidy | 12 --- .../klimov_m_torus/tests/performance/main.cpp | 48 +++++----- 5 files changed, 82 insertions(+), 76 deletions(-) delete mode 100644 tasks/klimov_m_torus/tests/.clang-tidy diff --git a/tasks/klimov_m_torus/common/include/common.hpp b/tasks/klimov_m_torus/common/include/common.hpp index 969775be..989b4983 100644 --- a/tasks/klimov_m_torus/common/include/common.hpp +++ b/tasks/klimov_m_torus/common/include/common.hpp @@ -8,8 +8,8 @@ namespace klimov_m_torus { struct TransferRequest { - int sender; - int receiver; + int sender{}; + int receiver{}; std::vector data; }; diff --git a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp index ae2e3df5..5143d7a2 100644 --- a/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp +++ b/tasks/klimov_m_torus/mpi/include/ops_mpi.hpp @@ -22,14 +22,14 @@ class TorusMeshCommunicator : public BaseTask { bool RunImpl() override; bool PostProcessingImpl() override; - static std::pair CalculateGridSize(int totalProcesses); + static std::pair CalculateGridSize(int total_processes); static int CombineCoordinates(int row, int col, int rows, int cols); static std::pair SplitRank(int rank, int cols); static std::vector BuildMessageRoute(int rows, int cols, int from, int to); void DistributeSenderReceiver(int &src, int &dst); void DistributeDataLength(int src, int &len) const; - std::vector AssembleSendBuffer(int src, int len) const; + [[nodiscard]] std::vector AssembleSendBuffer(int src, int len) const; void RelayMessage(int src, int dst, const std::vector &route, const std::vector &buffer, std::vector &output) const; void SaveFinalResult(int dst, const std::vector &output, const std::vector &route); diff --git a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp index 797ba30a..c61e073e 100644 --- a/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp +++ b/tasks/klimov_m_torus/mpi/src/ops_mpi.cpp @@ -12,21 +12,65 @@ namespace klimov_m_torus { +namespace { + +// Вспомогательные функции, вынесенные в анонимный namespace для снижения сложности RelayMessage + +void HandleSameNode(int current_rank, int src, const std::vector &buffer, std::vector &output) { + if (current_rank == src) { + output = buffer; + } +} + +void HandleSourceNode(int current_rank, int src, const std::vector &route, const std::vector &buffer, + std::vector &output) { + output = buffer; + if (current_rank == src && route.size() > 1) { + int next_hop = route[1]; + int send_len = static_cast(buffer.size()); + MPI_Send(&send_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); + if (send_len > 0) { + MPI_Send(output.data(), send_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); + } + } +} + +void HandleIntermediateNode(int current_rank, int dst, const std::vector &route, int my_pos, + std::vector &output) { + int prev_hop = route[my_pos - 1]; + int recv_len = 0; + MPI_Recv(&recv_len, 1, MPI_INT, prev_hop, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + output.resize(recv_len); + if (recv_len > 0) { + MPI_Recv(output.data(), recv_len, MPI_INT, prev_hop, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } + + if (current_rank != dst && my_pos + 1 < static_cast(route.size())) { + int next_hop = route[my_pos + 1]; + MPI_Send(&recv_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); + if (recv_len > 0) { + MPI_Send(output.data(), recv_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); + } + } +} + +} // namespace + TorusMeshCommunicator::TorusMeshCommunicator(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; GetOutput() = {}; } -std::pair TorusMeshCommunicator::CalculateGridSize(int totalProcesses) { - int rows = static_cast(std::sqrt(static_cast(totalProcesses))); - while (rows > 1 && (totalProcesses % rows != 0)) { +std::pair TorusMeshCommunicator::CalculateGridSize(int total_processes) { + int rows = static_cast(std::sqrt(static_cast(total_processes))); + while (rows > 1 && (total_processes % rows != 0)) { --rows; } if (rows <= 0) { rows = 1; } - int cols = totalProcesses / rows; + int cols = total_processes / rows; if (cols <= 0) { cols = 1; } @@ -119,7 +163,8 @@ bool TorusMeshCommunicator::PreProcessingImpl() { } bool TorusMeshCommunicator::RunImpl() { - int sender = 0, receiver = 0; + int sender = 0; + int receiver = 0; DistributeSenderReceiver(sender, receiver); int data_len = 0; @@ -155,48 +200,23 @@ void TorusMeshCommunicator::DistributeDataLength(int src, int &len) const { std::vector TorusMeshCommunicator::AssembleSendBuffer(int src, int len) const { std::vector buffer(len); if (current_rank_ == src && len > 0) { - std::copy(local_request_.data.begin(), local_request_.data.end(), buffer.begin()); + std::ranges::copy(local_request_.data, buffer.begin()); } return buffer; } void TorusMeshCommunicator::RelayMessage(int src, int dst, const std::vector &route, const std::vector &buffer, std::vector &output) const { - const int route_len = static_cast(route.size()); - auto it = std::find(route.begin(), route.end(), current_rank_); + auto it = std::ranges::find(route, current_rank_); bool on_route = (it != route.end()); int my_pos = on_route ? static_cast(std::distance(route.begin(), it)) : -1; if (src == dst) { - if (current_rank_ == src) { - output = buffer; - } + HandleSameNode(current_rank_, src, buffer, output); } else if (current_rank_ == src) { - output = buffer; - if (route_len > 1) { - int next_hop = route[1]; - int send_len = static_cast(buffer.size()); - MPI_Send(&send_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); - if (send_len > 0) { - MPI_Send(output.data(), send_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); - } - } + HandleSourceNode(current_rank_, src, route, buffer, output); } else if (on_route) { - int prev_hop = route[my_pos - 1]; - int recv_len = 0; - MPI_Recv(&recv_len, 1, MPI_INT, prev_hop, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - output.resize(recv_len); - if (recv_len > 0) { - MPI_Recv(output.data(), recv_len, MPI_INT, prev_hop, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - } - - if (current_rank_ != dst && my_pos + 1 < route_len) { - int next_hop = route[my_pos + 1]; - MPI_Send(&recv_len, 1, MPI_INT, next_hop, 0, MPI_COMM_WORLD); - if (recv_len > 0) { - MPI_Send(output.data(), recv_len, MPI_INT, next_hop, 1, MPI_COMM_WORLD); - } - } + HandleIntermediateNode(current_rank_, dst, route, my_pos, output); } } diff --git a/tasks/klimov_m_torus/tests/.clang-tidy b/tasks/klimov_m_torus/tests/.clang-tidy deleted file mode 100644 index 7fe3b6a9..00000000 --- a/tasks/klimov_m_torus/tests/.clang-tidy +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/tasks/klimov_m_torus/tests/performance/main.cpp b/tasks/klimov_m_torus/tests/performance/main.cpp index fe3d9055..17138009 100644 --- a/tasks/klimov_m_torus/tests/performance/main.cpp +++ b/tasks/klimov_m_torus/tests/performance/main.cpp @@ -12,50 +12,50 @@ namespace klimov_m_torus { class TorusPerformanceTest : public ppc::util::BaseRunPerfTests { protected: - InType test_data_; - bool data_ready_ = false; - int world_size_ = 1; - int rank_ = 0; - bool is_seq_mode_ = false; + InType test_data; + bool data_ready = false; + int world_size = 1; + int rank = 0; + bool is_seq_mode = false; void SetUp() override { std::string task_name = std::get<1>(GetParam()); - is_seq_mode_ = (task_name.find("seq") != std::string::npos); + is_seq_mode = (task_name.find("seq") != std::string::npos); int mpi_initialized = 0; MPI_Initialized(&mpi_initialized); if (mpi_initialized != 0) { - MPI_Comm_size(MPI_COMM_WORLD, &world_size_); - MPI_Comm_rank(MPI_COMM_WORLD, &rank_); + MPI_Comm_size(MPI_COMM_WORLD, &world_size); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); } PrepareTestData(); } void PrepareTestData() { - if (data_ready_) { + if (data_ready) { return; } const int data_size = 10000000; - test_data_.sender = 0; - if (is_seq_mode_) { - test_data_.receiver = 0; + test_data.sender = 0; + if (is_seq_mode) { + test_data.receiver = 0; } else { - test_data_.receiver = (world_size_ > 1) ? (world_size_ - 1) : 0; + test_data.receiver = (world_size > 1) ? (world_size - 1) : 0; } - test_data_.data.resize(data_size); + test_data.data.resize(data_size); for (int i = 0; i < data_size; ++i) { - test_data_.data[i] = i + 1; + test_data.data[i] = i + 1; } - data_ready_ = true; + data_ready = true; } InType GetTestInputData() override { - return test_data_; + return test_data; } bool CheckTestOutputData(OutType &out) override { @@ -63,28 +63,26 @@ class TorusPerformanceTest : public ppc::util::BaseRunPerfTests bool is_mpi = (task_name.find("mpi") != std::string::npos); if (is_mpi) { - if (rank_ != test_data_.receiver) { + if (rank != test_data.receiver) { return out.received_data.empty() && out.route.empty(); } - if (out.received_data.size() != test_data_.data.size()) { + if (out.received_data.size() != test_data.data.size()) { return false; } if (out.received_data.empty()) { return true; } - return (out.received_data.front() == test_data_.data.front() && - out.received_data.back() == test_data_.data.back()); + return (out.received_data.front() == test_data.data.front() && out.received_data.back() == test_data.data.back()); } - if (rank_ == 0) { - if (out.received_data.size() != test_data_.data.size()) { + if (rank == 0) { + if (out.received_data.size() != test_data.data.size()) { return false; } if (out.received_data.empty()) { return true; } - return (out.received_data.front() == test_data_.data.front() && - out.received_data.back() == test_data_.data.back()); + return (out.received_data.front() == test_data.data.front() && out.received_data.back() == test_data.data.back()); } return true; }