From bbb36f5b181d0a4b4d66fd37dc443b4bd5ec85c5 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 18:00:28 +0300 Subject: [PATCH 01/12] try_1.0 --- .../common/include/common.hpp | 18 ++ tasks/fatehov_k_matrix_crs/info.json | 9 + .../mpi/include/ops_mpi.hpp | 26 ++ .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 160 ++++++++++ tasks/fatehov_k_matrix_crs/report.md | 300 ++++++++++++++++++ .../seq/include/ops_seq.hpp | 25 ++ .../fatehov_k_matrix_crs/seq/src/ops_seq.cpp | 72 +++++ tasks/fatehov_k_matrix_crs/settings.json | 7 + tasks/fatehov_k_matrix_crs/tests/.clang-tidy | 13 + .../tests/functional/main.cpp | 103 ++++++ .../tests/performance/main.cpp | 116 +++++++ 11 files changed, 849 insertions(+) create mode 100644 tasks/fatehov_k_matrix_crs/common/include/common.hpp create mode 100644 tasks/fatehov_k_matrix_crs/info.json create mode 100644 tasks/fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp create mode 100644 tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp create mode 100644 tasks/fatehov_k_matrix_crs/report.md create mode 100644 tasks/fatehov_k_matrix_crs/seq/include/ops_seq.hpp create mode 100644 tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp create mode 100644 tasks/fatehov_k_matrix_crs/settings.json create mode 100644 tasks/fatehov_k_matrix_crs/tests/.clang-tidy create mode 100644 tasks/fatehov_k_matrix_crs/tests/functional/main.cpp create mode 100644 tasks/fatehov_k_matrix_crs/tests/performance/main.cpp diff --git a/tasks/fatehov_k_matrix_crs/common/include/common.hpp b/tasks/fatehov_k_matrix_crs/common/include/common.hpp new file mode 100644 index 0000000000..453dc456c9 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/common/include/common.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +#include "task/include/task.hpp" + +namespace fatehov_k_matrix_crs { + +using InType = std::tuple, std::vector, std::vector, + std::vector, std::vector, std::vector>; +using OutType = std::vector; +using TestType = std::tuple, std::vector, std::vector, + std::vector, std::vector, std::vector, std::vector>; +using BaseTask = ppc::task::Task; + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/info.json b/tasks/fatehov_k_matrix_crs/info.json new file mode 100644 index 0000000000..ca5a30c337 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/info.json @@ -0,0 +1,9 @@ +{ + "student": { + "first_name": "Камиль", + "last_name": "Фатехов", + "middle_name": "Гаярович", + "group_number": "3823Б1ФИ3", + "task_number": "3" + } +} diff --git a/tasks/fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp b/tasks/fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp new file mode 100644 index 0000000000..d6b05373ad --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "fatehov_k_matrix_crs/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace fatehov_k_matrix_crs { + +class FatehovKMatrixCRSMPI : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kMPI; + } + explicit FatehovKMatrixCRSMPI(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static const int kMaxRows = 10000; + static const int kMaxCols = 10000; + static const int kMaxNonZero = 10000000; +}; + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp new file mode 100644 index 0000000000..2325515c46 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -0,0 +1,160 @@ +#include "fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "fatehov_k_matrix_crs/common/include/common.hpp" + +namespace fatehov_k_matrix_crs { + +FatehovKMatrixCRSMPI::FatehovKMatrixCRSMPI(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool FatehovKMatrixCRSMPI::ValidationImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int is_valid = 0; + if (rank == 0) { + auto &data = GetInput(); + size_t rows = std::get<0>(data); + size_t cols = std::get<1>(data); + if (rows > 0 && cols > 0 && rows <= kMaxRows && cols <= kMaxCols) { + is_valid = 1; + } + } + MPI_Bcast(&is_valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + return is_valid == 1; +} + +bool FatehovKMatrixCRSMPI::PreProcessingImpl() { + return true; +} + +bool FatehovKMatrixCRSMPI::RunImpl() { + int rank; + int size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + size_t rows = 0; + size_t cols = 0; + if (rank == 0) { + rows = std::get<0>(GetInput()); + cols = std::get<1>(GetInput()); + } + MPI_Bcast(&rows, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(&cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + std::vector valB = {}; + std::vector colB{}; + std::vector ptrB(rows + 1, 0); + size_t nnzB = 0; + + if (rank == 0) { + valB = std::get<3>(GetInput()); + colB = std::get<5>(GetInput()); + ptrB = std::get<7>(GetInput()); + nnzB = valB.size(); + } + MPI_Bcast(&nnzB, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + if (rank != 0) { + valB.resize(nnzB); + colB.resize(nnzB); + } + + if (nnzB > 0) { + MPI_Bcast(valB.data(), (int)nnzB, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(colB.data(), (int)nnzB, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + } + MPI_Bcast(ptrB.data(), (int)rows + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + std::vector ptrA(rows + 1); + if (rank == 0) { + ptrA = std::get<6>(GetInput()); + } + MPI_Bcast(ptrA.data(), (int)rows + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + int rows_per_proc = (int)rows / size; + int rem = (int)rows % size; + int local_rows = rows_per_proc + (rank < rem ? 1 : 0); + int start_row = rank * rows_per_proc + std::min(rank, rem); + int end_row = start_row + local_rows; + + size_t local_nnz = ptrA[end_row] - ptrA[start_row]; + std::vector valA_loc(local_nnz); + std::vector colA_loc(local_nnz); + + if (rank == 0) { + const auto &vA = std::get<2>(GetInput()); + const auto &cA = std::get<4>(GetInput()); + for (int i = 1; i < size; ++i) { + int s = (i * rows_per_proc) + std::min(i, rem); + int e = s + rows_per_proc + (i < rem ? 1 : 0); + size_t sz = ptrA[e] - ptrA[s]; + if (sz > 0) { + MPI_Send(&vA[ptrA[s]], (int)sz, MPI_DOUBLE, i, 0, MPI_COMM_WORLD); + MPI_Send(&cA[ptrA[s]], (int)sz, MPI_UNSIGNED_LONG, i, 1, MPI_COMM_WORLD); + } + } + std::copy(vA.begin() + ptrA[start_row], vA.begin() + ptrA[end_row], valA_loc.begin()); + std::copy(cA.begin() + ptrA[start_row], cA.begin() + ptrA[end_row], colA_loc.begin()); + } else if (local_nnz > 0) { + MPI_Recv(valA_loc.data(), (int)local_nnz, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(colA_loc.data(), (int)local_nnz, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } + + std::vector res_loc(local_rows * cols, 0.0); + for (int i = 0; i < local_rows; ++i) { + size_t row_start_offset = ptrA[start_row + i] - ptrA[start_row]; + size_t row_end_offset = ptrA[start_row + i + 1] - ptrA[start_row]; + + for (size_t k = row_start_offset; k < row_end_offset; ++k) { + double a_val = valA_loc[k]; + size_t a_col = colA_loc[k]; + + for (size_t j = ptrB[a_col]; j < ptrB[a_col + 1]; ++j) { + res_loc[i * cols + colB[j]] += a_val * valB[j]; + } + } + } + + std::vector counts(size); + std::vector displs(size); + int send_cnt = local_rows * (int)cols; + MPI_Gather(&send_cnt, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); + + std::vector full_res = {}; + if (rank == 0) { + full_res.resize(rows * cols); + displs[0] = 0; + for (int i = 1; i < size; ++i) { + displs[i] = displs[i - 1] + counts[i - 1]; + } + } + + MPI_Gatherv(res_loc.data(), send_cnt, MPI_DOUBLE, full_res.data(), counts.data(), displs.data(), MPI_DOUBLE, 0, + MPI_COMM_WORLD); + + if (rank == 0) { + GetOutput() = std::move(full_res); + } else { + GetOutput().resize(rows * cols); + } + + MPI_Bcast(GetOutput().data(), (int)(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); + + return true; +} + +bool FatehovKMatrixCRSMPI::PostProcessingImpl() { + return true; +} + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md new file mode 100644 index 0000000000..53cfdb4bcc --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -0,0 +1,300 @@ +# Максимальное значение элементов матрицы + +- Студент: Фатехов Камиль Гаярович, группа 3823Б1ФИ3 +- Технология: SEQ | MPI +- Вариант: 9 + +## 1. Введение +**Цель лабораторной**: +Разработка последовательной и параллельной версии поиска максимального значения элементов матрицы с использованием решётки-тора в MPI и последующим сравнением этих двух версий. + +**Задачи лабораторной**: +- Реализовать последовательную версию поиска максимального значения элементов матрицы. +- Изучить основы MPI и реализовать параллельную версию программы с топологией решётки-тора. +- Сравнить производительность и эффективность этих реализаций. +## 2. Постановка задачи +**Задача**: Определить максимальное значение элементов матрицы после «утяжеления» вычислений. + +**Входные данные** + Данные представлены в виде `tuple(rows, columns, matrix)`, где: + - `rows` - количество строк матрицы (*size_t*) + - `columns` - количество столбцов матрицы (*size_t*) + - `matrix` - непосредственно сама матрица (*vector*) + +**Выходные данные:** +На выходе получается единичное значение типа *double* – максимальный элемент в матрице после обработки. + +**Ограничения**: + - `rows <= 10000` + - `cols <= 10000` + + +## 3. Базовый алгоритм (Последовательный) + + **Реализован следующий код**: +```cpp +bool FatehovKReshetkaTorSEQ::RunImpl() { + auto &data = GetInput(); + std::vector &matrix = std::get<2>(data); + double global_max = -1e18; + for (double val : matrix) { + double heavy_val = val; + for (int k = 0; k < 100; ++k) { + heavy_val = (std::sin(heavy_val) * std::cos(heavy_val)) + std::exp(std::complex(0, heavy_val).real()) + + std::sqrt(std::abs(heavy_val) + 1.0); + if (std::isinf(heavy_val)) { + heavy_val = val; + } + } + global_max = std::max(heavy_val, global_max); + } + GetOutput() = global_max; + return true; +} +``` + +**Алгоритм работы**: +1. Получаем входные данные: матрицу в виде вектора. + +2. Инициализируем максимум очень малым числом. + +3. Для каждого элемента матрицы выполняем «утяжеление» (100 итераций вычислений с тригонометрическими и другими функциями). + +4. Сравниваем полученное значение с текущим максимумом и обновляем его при необходимости. + +5. Возвращаем итоговый максимум. + +## 4. Схема распараллеливания + +**Описание**: +- Код реализует параллельный поиск максимального элемента в матрице с использованием MPI и топологии решётки-тора. Матрица разбивается на блоки по строкам и столбцам, каждый процесс обрабатывает свой блок, после чего выполняется редукция максимума по тору. + +**Алгоритм работы**: + +1. **Инициализация MPI и получение размеров матрицы** + +```cpp +MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); +MPI_Comm_size(MPI_COMM_WORLD, &world_size); + +size_t total_rows = 0; +size_t total_cols = 0; +std::vector global_matrix; + +if (world_rank == 0) { + auto &data = GetInput(); + total_rows = std::get<0>(data); + total_cols = std::get<1>(data); + global_matrix = std::get<2>(data); +} + +MPI_Bcast(&total_rows, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +MPI_Bcast(&total_cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +``` + +2. **Создание решётки-тора** +```cpp +CalculateGridDimensions(world_size, grid_rows, grid_cols); +// каждый процесс получает свои координаты в сетке +GetGridCoordinates(world_rank, grid_cols, row, col); +``` + +3. **Распределение данных по процессам** + +- Матрица разбивается на блоки по строкам и столбцам. + +- Каждый процесс получает свой локальный блок матрицы. + +- Распределение выполняется через MPI_Send/MPI_Recv. + +4. **Локальные вычисления** + +```cpp +double local_max = FindLocalMax(local_matrix); +// где FindLocalMax выполняет утяжеление и поиск максимума в локальном блоке + +``` +5. **Глобальная редукция через тор** +```cpp +TorusAllReduce(local_max, world_rank, grid_rows, grid_cols); +// выполняются попарные обмены по строкам и столбцам сетки +``` + +6. **Возврат результата** +```cpp +GetOutput() = global_max; +``` + +## 5. Детали реализации + +### **Утяжеление вычислений** + +Для увеличения вычислительной нагрузки каждый элемент матрицы обрабатывается 100 раз с использованием: + +- ``std::sin`` + +- ``std::cos`` + +- ``std::exp`` + +- ``std::sqrt`` + +- ``std::complex`` + +Это сделано для увеличения времени вычислений и лучшего сравнения SEQ и MPI версий. + +**Генерация тестовых данных**: +Для создания тестовой матрицы используется линейный конгруэнтный генератор (ЛКГ) - алгоритм для генерации псевдослучайных чисел, который обеспечивает детерминированность и воспроизводимость данных. + +```cpp +for (size_t i = 0; i < total; ++i) { + state = (a * state + c) % m; + double value = ((static_cast(state) / m) * 2000.0) - 1000.0; + matrix.push_back(value); +} +``` + +где: + +- `state` - текущее состояние генератора +- `a` - множитель (1664525) +- `c` - приращение (1013904223) +- `m` - модуль (2²² = 4194304) + +**Процесс генерации**: + +Процесс генерации: + +1. Инициализация: Начинаем с начального состояния state = 42 + +2. Итеративное вычисление: Для каждого элемента матрицы: + +- Вычисляем новое состояние по формуле: ``state = (a × state + c) mod m`` + +- Нормализуем значение: ``value = (state / m) × 2000.0 - 1000.0`` + +- Получаем число в диапазоне ``[-1000.0, 1000.0)`` + +- Сохраняем значение в матрице + +3. Размер матрицы: В тестах производительности используется матрица 1000×1000 элементов (1 миллион чисел) + +**Преимущества для тестирования**: + +- Генерирует одинаковые данные на всех процессах + +- Быстрый и простой алгоритм + +- Предсказуемый результат для проверки правильности + + + +## 6. Экспериментальная среда +- Hardware/OS: AMD RYZEN 5 5600 6-Core Processor, 12-Threads, 16GB, Ubuntu (DevContainer/WSL 2.0) +- Toolchain: GCC 14.1.0, cmake version 3.31.1, Release +- Data: происходит генерация данных через функцию, описанную в пункте 5 + +## 7. Результаты и обсуждение + +### 7.1 Корректность +**Модульные тесты** + +В коде реализовано 3 тестовых случая: + +- Матрица 3×4 с положительными числами + +- Матрица 3×3 с отрицательными числами + +- Матрица 5×5 со смешанными значениями + +**Условие корректности:** +SEQ и MPI версии дают идентичные результаты с точностью до ``1e-10``. + + +Таким образом, корректность подтверждается совпадением результатов с эталонными значениями, успешным прохождением модульных тестов и идентичностью результатов разных реализаций алгоритма. + +### 7.2 Производительность +- Тестирование проводилось на матрице 1000×1000 с утяжелением вычислений. + +| **Режим** | **Количество процессов** | **Время, с** | **Ускорение** | **Эффективность** | +|-------------|-------|---------|---------|------------| +| seq | 1 | 2.0917 | 1.00 | N/A | +| mpi | 2 | 1.0553 | 1.98 | 99.0% | +| mpi | 4 | 0.5460 | 3.83 | 95.75% | +| mpi | 6 | 0.3735 | 5.60 | 93.33% | + +**Анализ результатов**: + +**Высокая эффективность параллелизации**: + +- MPI версия демонстрирует почти линейное ускорение + +- Эффективность выше 93% даже для 6 процессов + +- Ускорение 5.60× на 6 процессах приближается к теоретическому максимуму + +**Причины высокой производительности MPI**: + +- Значительные вычисления на элемент: 100 итераций тяжёлых операций (тригонометрия, экспоненты, комплексные числа) + +- Оптимальное соотношение вычислений и коммуникаций: время вычислений существенно превосходит время обменов данными + +- Эффективная топология тора: минимизирует накладные расходы на коммуникации + +- Локальность данных: каждый процесс работает со своим блоком матрицы + +**Сравнение с SEQ версией**: + +- SEQ: 2.0917 с (один поток) + +- MPI (6 процессов): 0.3735 с (в 5.6 раз быстрее) + +- MPI (4 процесса): 0.5460 с (в 3.83 раз быстрее) + +**Масштабируемость**: + +- Рост числа процессов приводит к пропорциональному уменьшению времени выполнения + +- Незначительное падение эффективности (с 99% до 93%) объясняется увеличением коммуникационных затрат + +- Алгоритм хорошо масштабируется до 6 процессов + + + +## 8. Заключение + +В ходе лабораторной работы были успешно реализованы и сравнены последовательная и параллельная версии алгоритма поиска максимального значения элементов матрицы с учётом «утяжеления» вычислений. + +**Основные выводы**: + +1. **Корректность реализации**: + +- Обе версии (SEQ и MPI) дают идентичные результаты с высокой точностью + +- MPI версия с топологией решётки-тора корректно выполняет распределённые вычисления + +- Все модульные тесты успешно пройдены + +2. **Высокая эффективность MPI-реализации**: + +- Существенное ускорение: MPI версия с 6 процессами работает в 5.6 раза быстрее последовательной версии + +- Отличное масштабирование: почти линейное ускорение (ускорение 3.83× на 4 процессах, 5.60× на 6 процессах) + +- Высокая эффективность: 93-99% эффективности использования вычислительных ресурсов + +3. **Факторы успеха параллельной реализации**: + +- Значительная вычислительная нагрузка: 100 итераций сложных операций на элемент матрицы + +- Оптимальное соотношение вычислений/коммуникаций: время вычислений доминирует над временем обменов + +- Эффективная топология: решётка-тор минимизирует коммуникационные задержки + +- Сбалансированное распределение данных: равномерное распределение блоков матрицы по процессам + +## 9. Источники +[Линейный конгруэнтный генератор](https://www.tutorialspoint.com/cplusplus-program-to-implement-the-linear-congruential-generator-for-pseudo-random-number-generation) + + + diff --git a/tasks/fatehov_k_matrix_crs/seq/include/ops_seq.hpp b/tasks/fatehov_k_matrix_crs/seq/include/ops_seq.hpp new file mode 100644 index 0000000000..706e1817e0 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/seq/include/ops_seq.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "fatehov_k_matrix_crs/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace fatehov_k_matrix_crs { + +class FatehovKMatrixCRSSEQ : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSEQ; + } + explicit FatehovKMatrixCRSSEQ(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + static const int kMaxRows = 10000; + static const int kMaxCols = 10000; + static const int kMaxNonZero = 10000000; +}; + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp b/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp new file mode 100644 index 0000000000..94e2799794 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp @@ -0,0 +1,72 @@ +#include "fatehov_k_matrix_crs/seq/include/ops_seq.hpp" + +#include +#include +#include + +#include "fatehov_k_matrix_crs/common/include/common.hpp" + +namespace fatehov_k_matrix_crs { + +FatehovKMatrixCRSSEQ::FatehovKMatrixCRSSEQ(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; + GetOutput() = std::vector(); +} + +bool FatehovKMatrixCRSSEQ::ValidationImpl() { + auto &data = GetInput(); + size_t rows = std::get<0>(data); + size_t cols = std::get<1>(data); + auto &values = std::get<2>(data); + auto &values2 = std::get<3>(data); + auto &col_indices = std::get<4>(data); + auto &col_indices2 = std::get<5>(data); + auto &row_ptr = std::get<6>(data); + auto &row_ptr2 = std::get<7>(data); + + return (rows > 0 && rows <= kMaxRows) && (cols > 0 && cols <= kMaxCols) && (values.size() == col_indices.size()) && + (values2.size() == col_indices2.size()) && (values.size() <= kMaxNonZero) && (values2.size() <= kMaxNonZero) && + (row_ptr.size() == rows + 1) && (row_ptr2.size() == rows + 1) && (!values.empty()) && (!values2.empty()); +} + +bool FatehovKMatrixCRSSEQ::PreProcessingImpl() { + return true; +} + +bool FatehovKMatrixCRSSEQ::RunImpl() { + auto &data = GetInput(); + size_t rows = std::get<0>(data); + size_t cols = std::get<1>(data); + auto &values = std::get<2>(data); + auto &values2 = std::get<3>(data); + auto &col_indices = std::get<4>(data); + auto &col_indices2 = std::get<5>(data); + auto &row_ptr = std::get<6>(data); + auto &row_ptr2 = std::get<7>(data); + + std::vector result(rows * cols, 0.0); + + for (size_t i = 0; i < rows; ++i) { + for (size_t k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { + size_t colA = col_indices[k]; + double valA = values[k]; + + for (size_t j = row_ptr2[colA]; j < row_ptr2[colA + 1]; ++j) { + size_t colB = col_indices2[j]; + double valB = values2[j]; + + result[i * cols + colB] += valA * valB; + } + } + } + + GetOutput() = result; + return true; +} + +bool FatehovKMatrixCRSSEQ::PostProcessingImpl() { + return true; +} + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/settings.json b/tasks/fatehov_k_matrix_crs/settings.json new file mode 100644 index 0000000000..b1a0d52574 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/settings.json @@ -0,0 +1,7 @@ +{ + "tasks_type": "processes", + "tasks": { + "mpi": "enabled", + "seq": "enabled" + } +} diff --git a/tasks/fatehov_k_matrix_crs/tests/.clang-tidy b/tasks/fatehov_k_matrix_crs/tests/.clang-tidy new file mode 100644 index 0000000000..ef43b7aa8a --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/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/fatehov_k_matrix_crs/tests/functional/main.cpp b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp new file mode 100644 index 0000000000..718509191a --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp @@ -0,0 +1,103 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fatehov_k_matrix_crs/common/include/common.hpp" +#include "fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp" +#include "fatehov_k_matrix_crs/seq/include/ops_seq.hpp" +#include "util/include/func_test_util.hpp" +#include "util/include/util.hpp" + +namespace fatehov_k_matrix_crs { + +class FatehovKRunFuncTestsMatrixCRS : public ppc::util::BaseRunFuncTests { + public: + static std::string PrintTestParam(const TestType &test_param) { + std::string out = std::to_string(std::get<0>(test_param)) + "_matrix_" + std::to_string(std::get<1>(test_param)) + + "x" + std::to_string(std::get<2>(test_param)); + std::ranges::replace(out, '-', 'm'); + std::ranges::replace(out, '.', '_'); + return out; + } + + protected: + void SetUp() override { + TestType params = std::get(ppc::util::GTestParamIndex::kTestParams)>(GetParam()); + size_t rows = std::get<1>(params); + size_t cols = std::get<2>(params); + std::vector values = std::get<3>(params); + std::vector values2 = std::get<4>(params); + std::vector col_indices = std::get<5>(params); + std::vector col_indices2 = std::get<6>(params); + std::vector row_ptr = std::get<7>(params); + std::vector row_ptr2 = std::get<8>(params); + std::vector expected = std::get<9>(params); + + input_data_ = std::make_tuple(rows, cols, values, values2, col_indices, col_indices2, row_ptr, row_ptr2); + expected_result_ = expected; + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.size() != expected_result_.size()) { + std::cout << "Size mismatch: expected " << expected_result_.size() << ", got " << output_data.size() << std::endl; + return false; + } + + for (size_t i = 0; i < output_data.size(); ++i) { + if (std::fabs(expected_result_[i] - output_data[i]) > 1e-10) { + std::cout << "Mismatch at index " << i << ": expected " << expected_result_[i] << ", got " << output_data[i] + << std::endl; + return false; + } + } + return true; + } + + InType GetTestInputData() final { + return input_data_; + } + + private: + InType input_data_ = std::make_tuple(0, 0, std::vector{}, std::vector{}, std::vector{}, + std::vector{}, std::vector{}, std::vector{}); + OutType expected_result_ = std::vector{}; +}; + +namespace { + +TEST_P(FatehovKRunFuncTestsMatrixCRS, MatrixMultiplicationCRS) { + ExecuteTest(GetParam()); +} + +const std::array kTestParam = { + std::make_tuple(1, 2, 2, std::vector{1.0, 0.0, 0.0, 1.0}, std::vector{2.0, 3.0, 4.0, 5.0}, + std::vector{0, 1, 0, 1}, std::vector{0, 1, 0, 1}, std::vector{0, 2, 4}, + std::vector{0, 2, 4}, std::vector{2.0, 3.0, 4.0, 5.0}), + + std::make_tuple(2, 2, 2, std::vector{2.0, 0.0, 0.0, 2.0}, std::vector{1.0, 2.0, 3.0, 4.0}, + std::vector{0, 1, 0, 1}, std::vector{0, 1, 0, 1}, std::vector{0, 2, 4}, + std::vector{0, 2, 4}, std::vector{2.0, 4.0, 6.0, 8.0}), + + std::make_tuple(3, 2, 2, std::vector{1.0, 0.0, 0.0, 2.0}, std::vector{1.0, 2.0, 3.0, 4.0}, + std::vector{0, 1, 0, 1}, std::vector{0, 1, 0, 1}, std::vector{0, 2, 4}, + std::vector{0, 2, 4}, std::vector{1.0, 2.0, 6.0, 8.0})}; + +const auto kTestTasksList = + std::tuple_cat(ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_fatehov_k_matrix_crs), + ppc::util::AddFuncTask(kTestParam, PPC_SETTINGS_fatehov_k_matrix_crs)); + +const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); + +const auto kPerfTestName = FatehovKRunFuncTestsMatrixCRS::PrintFuncTestName; + +INSTANTIATE_TEST_SUITE_P(TestMatrixCRS, FatehovKRunFuncTestsMatrixCRS, kGtestValues, kPerfTestName); + +} // namespace + +} // namespace fatehov_k_matrix_crs diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp new file mode 100644 index 0000000000..c44990b971 --- /dev/null +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -0,0 +1,116 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fatehov_k_matrix_crs/common/include/common.hpp" +#include "fatehov_k_matrix_crs/mpi/include/ops_mpi.hpp" +#include "fatehov_k_matrix_crs/seq/include/ops_seq.hpp" +#include "util/include/perf_test_util.hpp" + +namespace fatehov_k_matrix_crs { + +class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests { + InType input_data_ = std::make_tuple(0, 0, std::vector{}, std::vector{}, std::vector{}, + std::vector{}, std::vector{}, std::vector{}); + OutType expected_result_ = std::vector{}; + + void SetUp() override { + const size_t rows = 5000; + const size_t cols = 5000; + const double sparsity = 0.02; + + std::vector values, values2; + std::vector col_indices, col_indices2; + std::vector row_ptr(rows + 1, 0), row_ptr2(rows + 1, 0); + + uint64_t state = 42; + const uint64_t a = 1664525ULL; + const uint64_t c = 1013904223ULL; + const uint64_t m = (1ULL << 22); + + for (size_t i = 0; i < rows; ++i) { + size_t nnz_in_row = 0; + for (size_t j = 0; j < cols; ++j) { + state = (a * state + c) % m; + if (static_cast(state % 1000) / 1000.0 < sparsity) { + double value = ((static_cast(state) / m) * 20.0) - 10.0; + values.push_back(value); + col_indices.push_back(j); + nnz_in_row++; + } + } + row_ptr[i + 1] = row_ptr[i] + nnz_in_row; + } + + state = 123; + for (size_t i = 0; i < rows; ++i) { + size_t nnz_in_row = 0; + for (size_t j = 0; j < cols; ++j) { + state = (a * state + c) % m; + if (static_cast(state % 1000) / 1000.0 < sparsity) { + double value = ((static_cast(state) / m) * 20.0) - 10.0; + values2.push_back(value); + col_indices2.push_back(j); + nnz_in_row++; + } + } + row_ptr2[i + 1] = row_ptr2[i] + nnz_in_row; + } + + std::vector computed_result(rows * cols, 0.0); + + for (size_t i = 0; i < rows; ++i) { + for (size_t k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { + size_t colA = col_indices[k]; + double valA = values[k]; + + for (size_t j = row_ptr2[colA]; j < row_ptr2[colA + 1]; ++j) { + size_t colB = col_indices2[j]; + double valB = values2[j]; + + computed_result[i * cols + colB] += valA * valB; + } + } + } + + expected_result_ = computed_result; + input_data_ = std::make_tuple(rows, cols, values, values2, col_indices, col_indices2, row_ptr, row_ptr2); + } + + bool CheckTestOutputData(OutType &output_data) final { + if (output_data.size() != expected_result_.size()) { + return false; + } + for (size_t i = 0; i < output_data.size(); ++i) { + if (std::fabs(expected_result_[i] - output_data[i]) > 1e-10) { + return false; + } + } + return true; + } + + InType GetTestInputData() final { + return input_data_; + } +}; + +TEST_P(FatehovKRunPerfTestsMatrixCRS, RunPerfModes) { + ExecuteTest(GetParam()); +} + +const auto kAllPerfTasks = + ppc::util::MakeAllPerfTasks(PPC_SETTINGS_fatehov_k_matrix_crs); + +const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); + +const auto kPerfTestName = FatehovKRunPerfTestsMatrixCRS::CustomPerfTestName; + +INSTANTIATE_TEST_SUITE_P(RunPerfTest, FatehovKRunPerfTestsMatrixCRS, kGtestValues, kPerfTestName); + +} // namespace fatehov_k_matrix_crs From 601be778a4f6388a3563075d86f775d2aea49ada Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 20:44:47 +0300 Subject: [PATCH 02/12] tidy fixes_1.0 --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 227 +++++++++++------- .../fatehov_k_matrix_crs/seq/src/ops_seq.cpp | 15 +- .../tests/functional/main.cpp | 8 +- .../tests/performance/main.cpp | 26 +- 4 files changed, 163 insertions(+), 113 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 2325515c46..160bfef4f9 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -3,134 +3,122 @@ #include #include -#include #include -#include +#include #include #include "fatehov_k_matrix_crs/common/include/common.hpp" namespace fatehov_k_matrix_crs { -FatehovKMatrixCRSMPI::FatehovKMatrixCRSMPI(const InType &in) { - SetTypeOfTask(GetStaticTypeOfTask()); - GetInput() = in; -} +namespace { -bool FatehovKMatrixCRSMPI::ValidationImpl() { - int rank = 0; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - int is_valid = 0; +void BroadcastMatrixSizes(size_t& rows, size_t& cols, int rank, const InType& input) { if (rank == 0) { - auto &data = GetInput(); - size_t rows = std::get<0>(data); - size_t cols = std::get<1>(data); - if (rows > 0 && cols > 0 && rows <= kMaxRows && cols <= kMaxCols) { - is_valid = 1; - } - } - MPI_Bcast(&is_valid, 1, MPI_INT, 0, MPI_COMM_WORLD); - return is_valid == 1; -} - -bool FatehovKMatrixCRSMPI::PreProcessingImpl() { - return true; -} - -bool FatehovKMatrixCRSMPI::RunImpl() { - int rank; - int size; - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - MPI_Comm_size(MPI_COMM_WORLD, &size); - - size_t rows = 0; - size_t cols = 0; - if (rank == 0) { - rows = std::get<0>(GetInput()); - cols = std::get<1>(GetInput()); + rows = std::get<0>(input); + cols = std::get<1>(input); } MPI_Bcast(&rows, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); MPI_Bcast(&cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +} - std::vector valB = {}; - std::vector colB{}; - std::vector ptrB(rows + 1, 0); - size_t nnzB = 0; - +void BroadcastMatrixB(std::vector& val_b, std::vector& col_b, std::vector& ptr_b, size_t& nnz_b, + size_t rows, int rank, const InType& input) { if (rank == 0) { - valB = std::get<3>(GetInput()); - colB = std::get<5>(GetInput()); - ptrB = std::get<7>(GetInput()); - nnzB = valB.size(); + val_b = std::get<3>(input); + col_b = std::get<5>(input); + ptr_b = std::get<7>(input); + nnz_b = val_b.size(); } - MPI_Bcast(&nnzB, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(&nnz_b, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); if (rank != 0) { - valB.resize(nnzB); - colB.resize(nnzB); + val_b.resize(nnz_b); + col_b.resize(nnz_b); } - - if (nnzB > 0) { - MPI_Bcast(valB.data(), (int)nnzB, MPI_DOUBLE, 0, MPI_COMM_WORLD); - MPI_Bcast(colB.data(), (int)nnzB, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + if (nnz_b > 0) { + MPI_Bcast(val_b.data(), static_cast(nnz_b), MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(col_b.data(), static_cast(nnz_b), MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); } - MPI_Bcast(ptrB.data(), (int)rows + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + ptr_b.resize(rows + 1); + MPI_Bcast(ptr_b.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +} - std::vector ptrA(rows + 1); +void BroadcastMatrixAStructure(std::vector& ptr_a, size_t rows, int rank, const InType& input) { + ptr_a.resize(rows + 1); if (rank == 0) { - ptrA = std::get<6>(GetInput()); + ptr_a = std::get<6>(input); } - MPI_Bcast(ptrA.data(), (int)rows + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +} - int rows_per_proc = (int)rows / size; - int rem = (int)rows % size; - int local_rows = rows_per_proc + (rank < rem ? 1 : 0); - int start_row = rank * rows_per_proc + std::min(rank, rem); - int end_row = start_row + local_rows; +void DistributeLocalWork(int& local_rows, int& start_row, int& end_row, size_t rows, int size, int rank) { + int rows_per_proc = static_cast(rows) / size; + int rem = static_cast(rows) % size; + local_rows = rows_per_proc + (rank < rem ? 1 : 0); + start_row = (rank * rows_per_proc) + std::min(rank, rem); + end_row = start_row + local_rows; +} - size_t local_nnz = ptrA[end_row] - ptrA[start_row]; - std::vector valA_loc(local_nnz); - std::vector colA_loc(local_nnz); +void ScatterMatrixA(std::vector& val_a_loc, std::vector& col_a_loc, const std::vector& ptr_a, + int start_row, int end_row, int rank, int size, const InType& input) { + size_t local_nnz = ptr_a[end_row] - ptr_a[start_row]; + val_a_loc.resize(local_nnz); + col_a_loc.resize(local_nnz); if (rank == 0) { - const auto &vA = std::get<2>(GetInput()); - const auto &cA = std::get<4>(GetInput()); + const auto& values_a = std::get<2>(input); + const auto& cols_a = std::get<4>(input); + for (int i = 1; i < size; ++i) { - int s = (i * rows_per_proc) + std::min(i, rem); - int e = s + rows_per_proc + (i < rem ? 1 : 0); - size_t sz = ptrA[e] - ptrA[s]; + int i_rows_per_proc = static_cast(ptr_a.size() - 1) / size; + int i_rem = static_cast(ptr_a.size() - 1) % size; + int i_local_rows = i_rows_per_proc + (i < i_rem ? 1 : 0); + int i_start_row = (i * i_rows_per_proc) + std::min(i, i_rem); + int i_end_row = i_start_row + i_local_rows; + size_t sz = ptr_a[i_end_row] - ptr_a[i_start_row]; if (sz > 0) { - MPI_Send(&vA[ptrA[s]], (int)sz, MPI_DOUBLE, i, 0, MPI_COMM_WORLD); - MPI_Send(&cA[ptrA[s]], (int)sz, MPI_UNSIGNED_LONG, i, 1, MPI_COMM_WORLD); + MPI_Send(&values_a[ptr_a[i_start_row]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); + MPI_Send(&cols_a[ptr_a[i_start_row]], static_cast(sz), MPI_UNSIGNED_LONG, i, 1, MPI_COMM_WORLD); } } - std::copy(vA.begin() + ptrA[start_row], vA.begin() + ptrA[end_row], valA_loc.begin()); - std::copy(cA.begin() + ptrA[start_row], cA.begin() + ptrA[end_row], colA_loc.begin()); + + std::copy(values_a.begin() + static_cast(ptr_a[start_row]), + values_a.begin() + static_cast(ptr_a[end_row]), val_a_loc.begin()); + std::copy(cols_a.begin() + static_cast(ptr_a[start_row]), + cols_a.begin() + static_cast(ptr_a[end_row]), col_a_loc.begin()); } else if (local_nnz > 0) { - MPI_Recv(valA_loc.data(), (int)local_nnz, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - MPI_Recv(colA_loc.data(), (int)local_nnz, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(val_a_loc.data(), static_cast(local_nnz), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(col_a_loc.data(), static_cast(local_nnz), MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); } +} - std::vector res_loc(local_rows * cols, 0.0); +void ComputeLocalResult(const std::vector& val_a_loc, const std::vector& col_a_loc, + const std::vector& val_b, const std::vector& col_b, + const std::vector& ptr_b, std::vector& res_loc, int local_rows, size_t cols, + const std::vector& ptr_a, int start_row) { for (int i = 0; i < local_rows; ++i) { - size_t row_start_offset = ptrA[start_row + i] - ptrA[start_row]; - size_t row_end_offset = ptrA[start_row + i + 1] - ptrA[start_row]; + size_t row_start_offset = ptr_a[start_row + i] - ptr_a[start_row]; + size_t row_end_offset = ptr_a[start_row + i + 1] - ptr_a[start_row]; for (size_t k = row_start_offset; k < row_end_offset; ++k) { - double a_val = valA_loc[k]; - size_t a_col = colA_loc[k]; + double a_val = val_a_loc[k]; + size_t a_col = col_a_loc[k]; - for (size_t j = ptrB[a_col]; j < ptrB[a_col + 1]; ++j) { - res_loc[i * cols + colB[j]] += a_val * valB[j]; + for (size_t j = ptr_b[a_col]; j < ptr_b[a_col + 1]; ++j) { + size_t index = static_cast(i) * cols + col_b[j]; + res_loc[index] += a_val * val_b[j]; } } } +} +void GatherResults(std::vector& full_res, const std::vector& res_loc, int local_rows, size_t rows, + size_t cols, int rank, int size, OutType& output) { std::vector counts(size); std::vector displs(size); - int send_cnt = local_rows * (int)cols; + int send_cnt = local_rows * static_cast(cols); MPI_Gather(&send_cnt, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); - std::vector full_res = {}; if (rank == 0) { full_res.resize(rows * cols); displs[0] = 0; @@ -143,12 +131,77 @@ bool FatehovKMatrixCRSMPI::RunImpl() { MPI_COMM_WORLD); if (rank == 0) { - GetOutput() = std::move(full_res); + output = std::move(full_res); } else { - GetOutput().resize(rows * cols); + output.resize(rows * cols); + } + + MPI_Bcast(output.data(), static_cast(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); +} + +} // namespace + +FatehovKMatrixCRSMPI::FatehovKMatrixCRSMPI(const InType& in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool FatehovKMatrixCRSMPI::ValidationImpl() { + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int is_valid = 0; + if (rank == 0) { + auto& data = GetInput(); + size_t rows = std::get<0>(data); + size_t cols = std::get<1>(data); + if (rows > 0 && cols > 0 && rows <= kMaxRows && cols <= kMaxCols) { + is_valid = 1; + } } + MPI_Bcast(&is_valid, 1, MPI_INT, 0, MPI_COMM_WORLD); + return is_valid == 1; +} + +bool FatehovKMatrixCRSMPI::PreProcessingImpl() { + return true; +} + +bool FatehovKMatrixCRSMPI::RunImpl() { + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + const auto& input = GetInput(); + + size_t rows = 0; + size_t cols = 0; + BroadcastMatrixSizes(rows, cols, rank, input); + + std::vector val_b{}; + std::vector col_b{}; + std::vector ptr_b{}; + size_t nnz_b = 0; + BroadcastMatrixB(val_b, col_b, ptr_b, nnz_b, rows, rank, input); + + std::vector ptr_a{}; + BroadcastMatrixAStructure(ptr_a, rows, rank, input); + + int local_rows = 0; + int start_row = 0; + int end_row = 0; + DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); + + std::vector val_a_loc{}; + std::vector col_a_loc{}; + ScatterMatrixA(val_a_loc, col_a_loc, ptr_a, start_row, end_row, rank, size, input); + + std::vector res_loc(static_cast(local_rows) * cols, 0.0); + ComputeLocalResult(val_a_loc, col_a_loc, val_b, col_b, ptr_b, res_loc, local_rows, cols, ptr_a, start_row); - MPI_Bcast(GetOutput().data(), (int)(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); + std::vector full_res{}; + auto& output = GetOutput(); + GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); return true; } diff --git a/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp b/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp index 94e2799794..150d69c2bc 100644 --- a/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp +++ b/tasks/fatehov_k_matrix_crs/seq/src/ops_seq.cpp @@ -1,7 +1,7 @@ #include "fatehov_k_matrix_crs/seq/include/ops_seq.hpp" -#include #include +#include #include #include "fatehov_k_matrix_crs/common/include/common.hpp" @@ -49,14 +49,15 @@ bool FatehovKMatrixCRSSEQ::RunImpl() { for (size_t i = 0; i < rows; ++i) { for (size_t k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { - size_t colA = col_indices[k]; - double valA = values[k]; + size_t col_a = col_indices[k]; + double val_a = values[k]; - for (size_t j = row_ptr2[colA]; j < row_ptr2[colA + 1]; ++j) { - size_t colB = col_indices2[j]; - double valB = values2[j]; + for (size_t j = row_ptr2[col_a]; j < row_ptr2[col_a + 1]; ++j) { + size_t col_b = col_indices2[j]; + double val_b = values2[j]; - result[i * cols + colB] += valA * valB; + size_t index = (i * cols) + col_b; + result[index] += val_a * val_b; } } } diff --git a/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp index 718509191a..d1285ca477 100644 --- a/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp @@ -1,8 +1,5 @@ #include -#include -#include -#include #include #include #include @@ -45,14 +42,11 @@ class FatehovKRunFuncTestsMatrixCRS : public ppc::util::BaseRunFuncTests 1e-10) { - std::cout << "Mismatch at index " << i << ": expected " << expected_result_[i] << ", got " << output_data[i] - << std::endl; return false; } } @@ -66,7 +60,7 @@ class FatehovKRunFuncTestsMatrixCRS : public ppc::util::BaseRunFuncTests{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}); - OutType expected_result_ = std::vector{}; + OutType expected_result_{}; }; namespace { diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp index c44990b971..948851efee 100644 --- a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -1,10 +1,8 @@ #include -#include #include #include #include -#include #include #include @@ -18,16 +16,19 @@ namespace fatehov_k_matrix_crs { class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests { InType input_data_ = std::make_tuple(0, 0, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}); - OutType expected_result_ = std::vector{}; + OutType expected_result_{}; void SetUp() override { const size_t rows = 5000; const size_t cols = 5000; const double sparsity = 0.02; - std::vector values, values2; - std::vector col_indices, col_indices2; - std::vector row_ptr(rows + 1, 0), row_ptr2(rows + 1, 0); + std::vector values{}; + std::vector values2{}; + std::vector col_indices{}; + std::vector col_indices2{}; + std::vector row_ptr(rows + 1, 0); + std::vector row_ptr2(rows + 1, 0); uint64_t state = 42; const uint64_t a = 1664525ULL; @@ -67,14 +68,15 @@ class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests Date: Sat, 3 Jan 2026 21:11:17 +0300 Subject: [PATCH 03/12] clang --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 160bfef4f9..4e099e556a 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -13,7 +13,7 @@ namespace fatehov_k_matrix_crs { namespace { -void BroadcastMatrixSizes(size_t& rows, size_t& cols, int rank, const InType& input) { +void BroadcastMatrixSizes(size_t &rows, size_t &cols, int rank, const InType &input) { if (rank == 0) { rows = std::get<0>(input); cols = std::get<1>(input); @@ -22,8 +22,8 @@ void BroadcastMatrixSizes(size_t& rows, size_t& cols, int rank, const InType& in MPI_Bcast(&cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); } -void BroadcastMatrixB(std::vector& val_b, std::vector& col_b, std::vector& ptr_b, size_t& nnz_b, - size_t rows, int rank, const InType& input) { +void BroadcastMatrixB(std::vector &val_b, std::vector &col_b, std::vector &ptr_b, size_t &nnz_b, + size_t rows, int rank, const InType &input) { if (rank == 0) { val_b = std::get<3>(input); col_b = std::get<5>(input); @@ -43,7 +43,7 @@ void BroadcastMatrixB(std::vector& val_b, std::vector& col_b, st MPI_Bcast(ptr_b.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); } -void BroadcastMatrixAStructure(std::vector& ptr_a, size_t rows, int rank, const InType& input) { +void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank, const InType &input) { ptr_a.resize(rows + 1); if (rank == 0) { ptr_a = std::get<6>(input); @@ -51,7 +51,7 @@ void BroadcastMatrixAStructure(std::vector& ptr_a, size_t rows, int rank MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); } -void DistributeLocalWork(int& local_rows, int& start_row, int& end_row, size_t rows, int size, int rank) { +void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t rows, int size, int rank) { int rows_per_proc = static_cast(rows) / size; int rem = static_cast(rows) % size; local_rows = rows_per_proc + (rank < rem ? 1 : 0); @@ -59,15 +59,15 @@ void DistributeLocalWork(int& local_rows, int& start_row, int& end_row, size_t r end_row = start_row + local_rows; } -void ScatterMatrixA(std::vector& val_a_loc, std::vector& col_a_loc, const std::vector& ptr_a, - int start_row, int end_row, int rank, int size, const InType& input) { +void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_loc, const std::vector &ptr_a, + int start_row, int end_row, int rank, int size, const InType &input) { size_t local_nnz = ptr_a[end_row] - ptr_a[start_row]; val_a_loc.resize(local_nnz); col_a_loc.resize(local_nnz); if (rank == 0) { - const auto& values_a = std::get<2>(input); - const auto& cols_a = std::get<4>(input); + const auto &values_a = std::get<2>(input); + const auto &cols_a = std::get<4>(input); for (int i = 1; i < size; ++i) { int i_rows_per_proc = static_cast(ptr_a.size() - 1) / size; @@ -92,10 +92,10 @@ void ScatterMatrixA(std::vector& val_a_loc, std::vector& col_a_l } } -void ComputeLocalResult(const std::vector& val_a_loc, const std::vector& col_a_loc, - const std::vector& val_b, const std::vector& col_b, - const std::vector& ptr_b, std::vector& res_loc, int local_rows, size_t cols, - const std::vector& ptr_a, int start_row) { +void ComputeLocalResult(const std::vector &val_a_loc, const std::vector &col_a_loc, + const std::vector &val_b, const std::vector &col_b, + const std::vector &ptr_b, std::vector &res_loc, int local_rows, size_t cols, + const std::vector &ptr_a, int start_row) { for (int i = 0; i < local_rows; ++i) { size_t row_start_offset = ptr_a[start_row + i] - ptr_a[start_row]; size_t row_end_offset = ptr_a[start_row + i + 1] - ptr_a[start_row]; @@ -112,8 +112,8 @@ void ComputeLocalResult(const std::vector& val_a_loc, const std::vector< } } -void GatherResults(std::vector& full_res, const std::vector& res_loc, int local_rows, size_t rows, - size_t cols, int rank, int size, OutType& output) { +void GatherResults(std::vector &full_res, const std::vector &res_loc, int local_rows, size_t rows, + size_t cols, int rank, int size, OutType &output) { std::vector counts(size); std::vector displs(size); int send_cnt = local_rows * static_cast(cols); @@ -141,7 +141,7 @@ void GatherResults(std::vector& full_res, const std::vector& res } // namespace -FatehovKMatrixCRSMPI::FatehovKMatrixCRSMPI(const InType& in) { +FatehovKMatrixCRSMPI::FatehovKMatrixCRSMPI(const InType &in) { SetTypeOfTask(GetStaticTypeOfTask()); GetInput() = in; } @@ -151,7 +151,7 @@ bool FatehovKMatrixCRSMPI::ValidationImpl() { MPI_Comm_rank(MPI_COMM_WORLD, &rank); int is_valid = 0; if (rank == 0) { - auto& data = GetInput(); + auto &data = GetInput(); size_t rows = std::get<0>(data); size_t cols = std::get<1>(data); if (rows > 0 && cols > 0 && rows <= kMaxRows && cols <= kMaxCols) { @@ -172,7 +172,7 @@ bool FatehovKMatrixCRSMPI::RunImpl() { MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); - const auto& input = GetInput(); + const auto &input = GetInput(); size_t rows = 0; size_t cols = 0; @@ -200,7 +200,7 @@ bool FatehovKMatrixCRSMPI::RunImpl() { ComputeLocalResult(val_a_loc, col_a_loc, val_b, col_b, ptr_b, res_loc, local_rows, cols, ptr_a, start_row); std::vector full_res{}; - auto& output = GetOutput(); + auto &output = GetOutput(); GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); return true; From 5cde6e5c9c3b2391f43c55dc3e5ffae8523abf30 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 22:10:10 +0300 Subject: [PATCH 04/12] report --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 2 +- tasks/fatehov_k_matrix_crs/report.md | 342 +++++++++--------- .../tests/functional/main.cpp | 5 +- .../tests/performance/main.cpp | 2 +- 4 files changed, 173 insertions(+), 178 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 4e099e556a..1e7fd1b2ff 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -105,7 +105,7 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< size_t a_col = col_a_loc[k]; for (size_t j = ptr_b[a_col]; j < ptr_b[a_col + 1]; ++j) { - size_t index = static_cast(i) * cols + col_b[j]; + size_t index = (static_cast(i) * cols) + col_b[j]; res_loc[index] += a_val * val_b[j]; } } diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md index 53cfdb4bcc..f5e512dd5e 100644 --- a/tasks/fatehov_k_matrix_crs/report.md +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -1,193 +1,186 @@ -# Максимальное значение элементов матрицы +# Умножение разреженных матриц в формате CRS - Студент: Фатехов Камиль Гаярович, группа 3823Б1ФИ3 - Технология: SEQ | MPI -- Вариант: 9 +- Вариант: 4 ## 1. Введение **Цель лабораторной**: -Разработка последовательной и параллельной версии поиска максимального значения элементов матрицы с использованием решётки-тора в MPI и последующим сравнением этих двух версий. +Разработка последовательной и параллельной версий умножения разреженных матриц в формате CRS (Compressed Row Storage) и последующее сравнение этих реализаций по производительности. **Задачи лабораторной**: -- Реализовать последовательную версию поиска максимального значения элементов матрицы. -- Изучить основы MPI и реализовать параллельную версию программы с топологией решётки-тора. -- Сравнить производительность и эффективность этих реализаций. +- Реализовать последовательную версию умножения разреженных матриц в формате CRS. + +- Изучить механизмы распределения данных в MPI и реализовать параллельную версию алгоритма. + +- Сравнить производительность и эффективность SEQ и MPI реализаций на различных размерах матриц. ## 2. Постановка задачи -**Задача**: Определить максимальное значение элементов матрицы после «утяжеления» вычислений. +**Задача**: Выполнить умножение двух разреженных матриц `A` `(M×K)` и `B` `(K×N)` с получением матрицы `C` `(M×N)`. **Входные данные** - Данные представлены в виде `tuple(rows, columns, matrix)`, где: - - `rows` - количество строк матрицы (*size_t*) - - `columns` - количество столбцов матрицы (*size_t*) - - `matrix` - непосредственно сама матрица (*vector*) + Данные представлены в виде кортежа: + ```cpp + tuple(rows_A, cols_B, + values_A, values_B, + col_indices_A, col_indices_B, + row_ptr_A, row_ptr_B) +``` +где: + +- `rows_A` - количество строк матрицы A (size_t) + +- `cols_B` - количество столбцов матрицы B (size_t) + +- `values_A`, `values_B` - ненулевые значения матриц (vector) + +- `col_indices_A`, `col_indices_B` - индексы столбцов ненулевых элементов `(vector)` -**Выходные данные:** -На выходе получается единичное значение типа *double* – максимальный элемент в матрице после обработки. +- ` row_ptr_A`, `row_ptr_B` - указатели на начало строк в массивах значений `(vector)` -**Ограничения**: - - `rows <= 10000` - - `cols <= 10000` +**Выходные данные:** +Плотная матрица `C` размером `M×N`, представленная в виде вектора `(vector)`, хранящего элементы по строкам. + +**Ограничения:** + +- `rows_A` <= `10000` + +- `cols_B` <= `10000` + +Общее количество ненулевых элементов в каждой матрице ≤ `10000000` ## 3. Базовый алгоритм (Последовательный) **Реализован следующий код**: ```cpp -bool FatehovKReshetkaTorSEQ::RunImpl() { + bool FatehovKMatrixCRSSEQ::RunImpl() { auto &data = GetInput(); - std::vector &matrix = std::get<2>(data); - double global_max = -1e18; - for (double val : matrix) { - double heavy_val = val; - for (int k = 0; k < 100; ++k) { - heavy_val = (std::sin(heavy_val) * std::cos(heavy_val)) + std::exp(std::complex(0, heavy_val).real()) + - std::sqrt(std::abs(heavy_val) + 1.0); - if (std::isinf(heavy_val)) { - heavy_val = val; + size_t rows = std::get<0>(data); + size_t cols = std::get<1>(data); + auto &values = std::get<2>(data); + auto &values2 = std::get<3>(data); + auto &col_indices = std::get<4>(data); + auto &col_indices2 = std::get<5>(data); + auto &row_ptr = std::get<6>(data); + auto &row_ptr2 = std::get<7>(data); + + std::vector result(rows * cols, 0.0); + + for (size_t i = 0; i < rows; ++i) { + for (size_t k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { + size_t col_a = col_indices[k]; + double val_a = values[k]; + + for (size_t j = row_ptr2[col_a]; j < row_ptr2[col_a + 1]; ++j) { + size_t col_b = col_indices2[j]; + double val_b = values2[j]; + + size_t index = (i * cols) + col_b; + result[index] += val_a * val_b; } } - global_max = std::max(heavy_val, global_max); } - GetOutput() = global_max; + + GetOutput() = result; return true; } ``` **Алгоритм работы**: -1. Получаем входные данные: матрицу в виде вектора. +1. Инициализируем результирующую матрицу C нулями размером M×N. + +2. Для каждой строки i матрицы A: -2. Инициализируем максимум очень малым числом. +- Для каждого ненулевого элемента в строке i матрицы A (значение val_a в столбце col_a): -3. Для каждого элемента матрицы выполняем «утяжеление» (100 итераций вычислений с тригонометрическими и другими функциями). +- Для каждого ненулевого элемента в строке col_a матрицы B (значение val_b в столбце col_b): -4. Сравниваем полученное значение с текущим максимумом и обновляем его при необходимости. +- Увеличиваем элемент C[i][col_b] на val_a × val_b -5. Возвращаем итоговый максимум. +3. Возвращаем результирующую матрицу C. ## 4. Схема распараллеливания **Описание**: -- Код реализует параллельный поиск максимального элемента в матрице с использованием MPI и топологии решётки-тора. Матрица разбивается на блоки по строкам и столбцам, каждый процесс обрабатывает свой блок, после чего выполняется редукция максимума по тору. +- MPI-версия распределяет строки матрицы A между процессами. Каждый процесс умножает свои строки на всю матрицу B, после чего результаты собираются на процессе 0 и рассылаются всем процессам. **Алгоритм работы**: -1. **Инициализация MPI и получение размеров матрицы** +1. **Инициализация MPI и получение размеров матриц** ```cpp -MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); -MPI_Comm_size(MPI_COMM_WORLD, &world_size); - -size_t total_rows = 0; -size_t total_cols = 0; -std::vector global_matrix; - -if (world_rank == 0) { - auto &data = GetInput(); - total_rows = std::get<0>(data); - total_cols = std::get<1>(data); - global_matrix = std::get<2>(data); -} - -MPI_Bcast(&total_rows, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); -MPI_Bcast(&total_cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); +MPI_Comm_rank(MPI_COMM_WORLD, &rank); +MPI_Comm_size(MPI_COMM_WORLD, &size); +BroadcastMatrixSizes(rows, cols, rank, input); ``` -2. **Создание решётки-тора** +2. **Рассылка структуры матрицы B всем процессам** ```cpp -CalculateGridDimensions(world_size, grid_rows, grid_cols); -// каждый процесс получает свои координаты в сетке -GetGridCoordinates(world_rank, grid_cols, row, col); +BroadcastMatrixB(val_b, col_b, ptr_b, nnz_b, rows, rank, input); ``` -3. **Распределение данных по процессам** - -- Матрица разбивается на блоки по строкам и столбцам. +3. **Распределение строк матрицы A по процессам** -- Каждый процесс получает свой локальный блок матрицы. - -- Распределение выполняется через MPI_Send/MPI_Recv. +```cpp +DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); +``` -4. **Локальные вычисления** +4. **Рассылка локальных частей матрицы A каждому процессу** ```cpp -double local_max = FindLocalMax(local_matrix); -// где FindLocalMax выполняет утяжеление и поиск максимума в локальном блоке - +ScatterMatrixA(val_a_loc, col_a_loc, ptr_a, start_row, end_row, rank, size, input); ``` -5. **Глобальная редукция через тор** +5. **Локальное умножение** ```cpp -TorusAllReduce(local_max, world_rank, grid_rows, grid_cols); -// выполняются попарные обмены по строкам и столбцам сетки +ComputeLocalResult(val_a_loc, col_a_loc, val_b, col_b, ptr_b, res_loc, local_rows, cols, ptr_a, start_row); ``` -6. **Возврат результата** +6. **Сбор и рассылка результатов** ```cpp -GetOutput() = global_max; +GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); ``` ## 5. Детали реализации -### **Утяжеление вычислений** - -Для увеличения вычислительной нагрузки каждый элемент матрицы обрабатывается 100 раз с использованием: - -- ``std::sin`` +### **Формат CRS (Compressed Row Storage)** -- ``std::cos`` +Каждая матрица хранится в трёх массивах: -- ``std::exp`` +- `values` - ненулевые значения -- ``std::sqrt`` +- `col_indices` - индексы столбцов для каждого значения -- ``std::complex`` - -Это сделано для увеличения времени вычислений и лучшего сравнения SEQ и MPI версий. - -**Генерация тестовых данных**: -Для создания тестовой матрицы используется линейный конгруэнтный генератор (ЛКГ) - алгоритм для генерации псевдослучайных чисел, который обеспечивает детерминированность и воспроизводимость данных. +- `row_ptr` - указатели на начало каждой строки в массивах значений и индексов +**Генерация тестовых данных:** +- Для создания разреженных матриц используется линейный конгруэнтный генератор с заданной разреженностью: ```cpp -for (size_t i = 0; i < total; ++i) { - state = (a * state + c) % m; - double value = ((static_cast(state) / m) * 2000.0) - 1000.0; - matrix.push_back(value); -} + uint64_t state = 42; + const uint64_t a = 1664525ULL; + const uint64_t c = 1013904223ULL; + const uint64_t m = (1ULL << 22); + + for (size_t i = 0; i < rows; ++i) { + size_t nnz_in_row = 0; + for (size_t j = 0; j < cols; ++j) { + state = (a * state + c) % m; + if (static_cast(state % 1000) / 1000.0 < sparsity) { + double value = ((static_cast(state) / m) * 20.0) - 10.0; + values.push_back(value); + col_indices.push_back(j); + nnz_in_row++; + } + } + row_ptr[i + 1] = row_ptr[i] + nnz_in_row; + } ``` +**Параметры генерации для тестов производительности:** -где: - -- `state` - текущее состояние генератора -- `a` - множитель (1664525) -- `c` - приращение (1013904223) -- `m` - модуль (2²² = 4194304) - -**Процесс генерации**: - -Процесс генерации: - -1. Инициализация: Начинаем с начального состояния state = 42 - -2. Итеративное вычисление: Для каждого элемента матрицы: - -- Вычисляем новое состояние по формуле: ``state = (a × state + c) mod m`` - -- Нормализуем значение: ``value = (state / m) × 2000.0 - 1000.0`` - -- Получаем число в диапазоне ``[-1000.0, 1000.0)`` - -- Сохраняем значение в матрице - -3. Размер матрицы: В тестах производительности используется матрица 1000×1000 элементов (1 миллион чисел) - -**Преимущества для тестирования**: - -- Генерирует одинаковые данные на всех процессах - -- Быстрый и простой алгоритм - -- Предсказуемый результат для проверки правильности +- Размер матриц: `5000×5000` +- Разреженность: `2%` +- Примерное количество ненулевых элементов: `5000×5000×0.02` = `500,000` на матрицу ## 6. Экспериментальная среда - Hardware/OS: AMD RYZEN 5 5600 6-Core Processor, 12-Threads, 16GB, Ubuntu (DevContainer/WSL 2.0) @@ -199,99 +192,98 @@ for (size_t i = 0; i < total; ++i) { ### 7.1 Корректность **Модульные тесты** -В коде реализовано 3 тестовых случая: +Реализовано 3 тестовых случая: -- Матрица 3×4 с положительными числами +- Матрицы 2×2 (единичная матрица × произвольная) -- Матрица 3×3 с отрицательными числами +- Матрицы 2×2 с умножением на скаляр -- Матрица 5×5 со смешанными значениями +- Матрицы 2×2 со смешанными значениями **Условие корректности:** -SEQ и MPI версии дают идентичные результаты с точностью до ``1e-10``. +- SEQ и MPI версии дают идентичные результаты с точностью до `1e-10`. + **Результаты тестирования:** -Таким образом, корректность подтверждается совпадением результатов с эталонными значениями, успешным прохождением модульных тестов и идентичностью результатов разных реализаций алгоритма. +- Все тестовые случаи успешно пройдены -### 7.2 Производительность -- Тестирование проводилось на матрице 1000×1000 с утяжелением вычислений. +- SEQ и MPI реализации выдают идентичные результаты -| **Режим** | **Количество процессов** | **Время, с** | **Ускорение** | **Эффективность** | -|-------------|-------|---------|---------|------------| -| seq | 1 | 2.0917 | 1.00 | N/A | -| mpi | 2 | 1.0553 | 1.98 | 99.0% | -| mpi | 4 | 0.5460 | 3.83 | 95.75% | -| mpi | 6 | 0.3735 | 5.60 | 93.33% | - -**Анализ результатов**: - -**Высокая эффективность параллелизации**: - -- MPI версия демонстрирует почти линейное ускорение - -- Эффективность выше 93% даже для 6 процессов - -- Ускорение 5.60× на 6 процессах приближается к теоретическому максимуму - -**Причины высокой производительности MPI**: +- Обработка граничных случаев корректна -- Значительные вычисления на элемент: 100 итераций тяжёлых операций (тригонометрия, экспоненты, комплексные числа) +### 7.2 Производительность +- Тестирование проводилось на матрицах 5000×5000 с разреженностью 2% -- Оптимальное соотношение вычислений и коммуникаций: время вычислений существенно превосходит время обменов данными +- Каждая матрица содержит примерно 500,000 ненулевых элементов -- Эффективная топология тора: минимизирует накладные расходы на коммуникации +| **Режим** | **Количество процессов** | **Время, с** | **Ускорение** | **Эффективность** | +|-------------|-------|---------|---------|------------| +| seq | 1 | 0.5484 | 1.00 | N/A | +| mpi | 2 | 0.4212 | 1.31 | 65.5% | +| mpi | 4 | 0.4970 | 1.10 | 27.5% | +| mpi | 6 | 0.5160 | 1.06 | 17.6% | -- Локальность данных: каждый процесс работает со своим блоком матрицы +**Анализ результатов:** -**Сравнение с SEQ версией**: +**Неэффективная параллелизация:** -- SEQ: 2.0917 с (один поток) +- MPI версия показывает очень низкое ускорение -- MPI (6 процессов): 0.3735 с (в 5.6 раз быстрее) +- Эффективность падает с увеличением числа процессов -- MPI (4 процесса): 0.5460 с (в 3.83 раз быстрее) +- На 6 процессах ускорение всего 1.06× (почти нет преимущества над SEQ) -**Масштабируемость**: +- Причины низкой производительности MPI: -- Рост числа процессов приводит к пропорциональному уменьшению времени выполнения + **Неоптимальное соотношение вычислений/коммуникаций:** -- Незначительное падение эффективности (с 99% до 93%) объясняется увеличением коммуникационных затрат +- Время вычислений: `O(nnz_A × avg_nnz_per_row_B)` для локальных данных -- Алгоритм хорошо масштабируется до 6 процессов +- Время коммуникаций: рассылка всей матрицы `B` + сбор полной матрицы `C` +- При разреженности `2%` и размере `5000×5000`, матрица `C` имеет `25` млн элементов (200 МБ данных) +**Высокие коммуникационные затраты:** -## 8. Заключение +```cpp +// Рассылка всей матрицы B всем процессам +MPI_Bcast(val_b.data(), nnz_b, MPI_DOUBLE, 0, MPI_COMM_WORLD); +// Сбор и рассылка полной матрицы C +MPI_Gatherv(...); // 25 млн элементов +MPI_Bcast(output.data(), rows * cols, MPI_DOUBLE, 0, MPI_COMM_WORLD); +``` +**Пересылка избыточных данных:** -В ходе лабораторной работы были успешно реализованы и сравнены последовательная и параллельная версии алгоритма поиска максимального значения элементов матрицы с учётом «утяжеления» вычислений. +- Каждый процесс получает всю матрицу B (500,000 элементов) -**Основные выводы**: +- Каждый процесс в итоге получает всю матрицу C (25 млн элементов) -1. **Корректность реализации**: +**Ограниченный параллелизм алгоритма:** -- Обе версии (SEQ и MPI) дают идентичные результаты с высокой точностью +- Умножение разреженных матриц имеет сложную структуру зависимостей -- MPI версия с топологией решётки-тора корректно выполняет распределённые вычисления +- Простое распределение строк не учитывает реальную вычислительную нагрузку -- Все модульные тесты успешно пройдены +- Некоторые строки могут быть почти пустыми, другие - плотными -2. **Высокая эффективность MPI-реализации**: +**Сравнение с SEQ версией:** -- Существенное ускорение: MPI версия с 6 процессами работает в 5.6 раза быстрее последовательной версии +- SEQ: 0.5484 с (один поток) -- Отличное масштабирование: почти линейное ускорение (ускорение 3.83× на 4 процессах, 5.60× на 6 процессах) +- MPI (2 процесса): 0.4212 с (ускорение всего 1.31×) -- Высокая эффективность: 93-99% эффективности использования вычислительных ресурсов +- MPI (6 процессов): 0.5160 с (медленнее чем на 4 процессах) -3. **Факторы успеха параллельной реализации**: +**Проблемы масштабируемости:** -- Значительная вычислительная нагрузка: 100 итераций сложных операций на элемент матрицы +- Рост числа процессов увеличивает коммуникационные накладные расходы -- Оптимальное соотношение вычислений/коммуникаций: время вычислений доминирует над временем обменов +- Ускорение достигает пика на 2 процессах, затем снижается -- Эффективная топология: решётка-тор минимизирует коммуникационные задержки +- При 6 процессах MPI версия почти не дает преимущества над SEQ -- Сбалансированное распределение данных: равномерное распределение блоков матрицы по процессам +## 8. Заключение +В ходе лабораторной работы были успешно реализованы последовательная и параллельная версии алгоритма умножения разреженных матриц в формате CRS. Однако результаты тестирования производительности выявили серьезные проблемы эффективности MPI-реализации. ## 9. Источники [Линейный конгруэнтный генератор](https://www.tutorialspoint.com/cplusplus-program-to-implement-the-linear-congruential-generator-for-pseudo-random-number-generation) diff --git a/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp index d1285ca477..e86c34cf57 100644 --- a/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/functional/main.cpp @@ -1,5 +1,8 @@ #include +#include +#include +#include #include #include #include @@ -60,7 +63,7 @@ class FatehovKRunFuncTestsMatrixCRS : public ppc::util::BaseRunFuncTests{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}); - OutType expected_result_{}; + OutType expected_result_; }; namespace { diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp index 948851efee..db82610b31 100644 --- a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -16,7 +16,7 @@ namespace fatehov_k_matrix_crs { class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests { InType input_data_ = std::make_tuple(0, 0, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}, std::vector{}); - OutType expected_result_{}; + OutType expected_result_; void SetUp() override { const size_t rows = 5000; From 96ea0ea55aadb76c96c14cfe935857d57bf0fc25 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 23:19:46 +0300 Subject: [PATCH 05/12] sizes --- tasks/fatehov_k_matrix_crs/report.md | 6 +++--- tasks/fatehov_k_matrix_crs/tests/performance/main.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md index f5e512dd5e..fe8de48696 100644 --- a/tasks/fatehov_k_matrix_crs/report.md +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -176,11 +176,11 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); ``` **Параметры генерации для тестов производительности:** -- Размер матриц: `5000×5000` +- Размер матриц: `1000×1000` - Разреженность: `2%` -- Примерное количество ненулевых элементов: `5000×5000×0.02` = `500,000` на матрицу +- Примерное количество ненулевых элементов: `1000×1000×0.02` = `100,000` на матрицу ## 6. Экспериментальная среда - Hardware/OS: AMD RYZEN 5 5600 6-Core Processor, 12-Threads, 16GB, Ubuntu (DevContainer/WSL 2.0) @@ -212,7 +212,7 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); - Обработка граничных случаев корректна ### 7.2 Производительность -- Тестирование проводилось на матрицах 5000×5000 с разреженностью 2% +- Тестирование проводилось на матрицах 1000×1000 с разреженностью 2% - Каждая матрица содержит примерно 500,000 ненулевых элементов diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp index db82610b31..972159ec51 100644 --- a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -19,8 +19,8 @@ class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests values{}; From 1d17c47ab0ea21b1507375545e2fad0c242fd0f3 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 23:38:53 +0300 Subject: [PATCH 06/12] sizes_2.0 --- tasks/fatehov_k_matrix_crs/report.md | 11 ++++------- tasks/fatehov_k_matrix_crs/tests/performance/main.cpp | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md index fe8de48696..d5b6da0143 100644 --- a/tasks/fatehov_k_matrix_crs/report.md +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -176,11 +176,11 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); ``` **Параметры генерации для тестов производительности:** -- Размер матриц: `1000×1000` +- Размер матриц: `100×100` - Разреженность: `2%` -- Примерное количество ненулевых элементов: `1000×1000×0.02` = `100,000` на матрицу +- Примерное количество ненулевых элементов: `100×100×0.02` = `10000` на матрицу ## 6. Экспериментальная среда - Hardware/OS: AMD RYZEN 5 5600 6-Core Processor, 12-Threads, 16GB, Ubuntu (DevContainer/WSL 2.0) @@ -212,9 +212,7 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); - Обработка граничных случаев корректна ### 7.2 Производительность -- Тестирование проводилось на матрицах 1000×1000 с разреженностью 2% -- Каждая матрица содержит примерно 500,000 ненулевых элементов | **Режим** | **Количество процессов** | **Время, с** | **Ускорение** | **Эффективность** | |-------------|-------|---------|---------|------------| @@ -241,7 +239,6 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); - Время коммуникаций: рассылка всей матрицы `B` + сбор полной матрицы `C` -- При разреженности `2%` и размере `5000×5000`, матрица `C` имеет `25` млн элементов (200 МБ данных) **Высокие коммуникационные затраты:** @@ -254,9 +251,9 @@ MPI_Bcast(output.data(), rows * cols, MPI_DOUBLE, 0, MPI_COMM_WORLD); ``` **Пересылка избыточных данных:** -- Каждый процесс получает всю матрицу B (500,000 элементов) +- Каждый процесс получает всю матрицу B -- Каждый процесс в итоге получает всю матрицу C (25 млн элементов) +- Каждый процесс в итоге получает всю матрицу C **Ограниченный параллелизм алгоритма:** diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp index 972159ec51..f235fe517d 100644 --- a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -19,8 +19,8 @@ class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests values{}; From fcd9ee49f1d6660e10350c230265beca999d8742 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 3 Jan 2026 23:55:18 +0300 Subject: [PATCH 07/12] fix_1.0 --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 113 +++++++++++++++--- 1 file changed, 97 insertions(+), 16 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 1e7fd1b2ff..0cf381c6d7 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -51,16 +51,42 @@ void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); } +// ИСПРАВЛЕННАЯ функция! void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t rows, int size, int rank) { int rows_per_proc = static_cast(rows) / size; int rem = static_cast(rows) % size; + + // Количество строк для текущего процесса local_rows = rows_per_proc + (rank < rem ? 1 : 0); - start_row = (rank * rows_per_proc) + std::min(rank, rem); + + // Начальная строка - сумма строк всех предыдущих процессов + start_row = 0; + for (int i = 0; i < rank; ++i) { + int i_rows = rows_per_proc + (i < rem ? 1 : 0); + start_row += i_rows; + } + + // Конечная строка (не включая) end_row = start_row + local_rows; + + // Проверка границ + if (end_row > static_cast(rows)) { + end_row = static_cast(rows); + } + if (start_row > end_row) { + start_row = end_row; + } } void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_loc, const std::vector &ptr_a, int start_row, int end_row, int rank, int size, const InType &input) { + // Проверка границ + if (start_row < 0 || end_row > static_cast(ptr_a.size() - 1)) { + val_a_loc.clear(); + col_a_loc.clear(); + return; + } + size_t local_nnz = ptr_a[end_row] - ptr_a[start_row]; val_a_loc.resize(local_nnz); col_a_loc.resize(local_nnz); @@ -70,11 +96,21 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l const auto &cols_a = std::get<4>(input); for (int i = 1; i < size; ++i) { + // Пересчитываем для каждого процесса int i_rows_per_proc = static_cast(ptr_a.size() - 1) / size; int i_rem = static_cast(ptr_a.size() - 1) % size; int i_local_rows = i_rows_per_proc + (i < i_rem ? 1 : 0); - int i_start_row = (i * i_rows_per_proc) + std::min(i, i_rem); + int i_start_row = 0; + for (int j = 0; j < i; ++j) { + int j_rows = i_rows_per_proc + (j < i_rem ? 1 : 0); + i_start_row += j_rows; + } int i_end_row = i_start_row + i_local_rows; + + if (i_end_row > static_cast(ptr_a.size() - 1)) { + i_end_row = static_cast(ptr_a.size() - 1); + } + size_t sz = ptr_a[i_end_row] - ptr_a[i_start_row]; if (sz > 0) { MPI_Send(&values_a[ptr_a[i_start_row]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); @@ -82,10 +118,12 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l } } - std::copy(values_a.begin() + static_cast(ptr_a[start_row]), - values_a.begin() + static_cast(ptr_a[end_row]), val_a_loc.begin()); - std::copy(cols_a.begin() + static_cast(ptr_a[start_row]), - cols_a.begin() + static_cast(ptr_a[end_row]), col_a_loc.begin()); + if (local_nnz > 0) { + std::copy(values_a.begin() + static_cast(ptr_a[start_row]), + values_a.begin() + static_cast(ptr_a[end_row]), val_a_loc.begin()); + std::copy(cols_a.begin() + static_cast(ptr_a[start_row]), + cols_a.begin() + static_cast(ptr_a[end_row]), col_a_loc.begin()); + } } else if (local_nnz > 0) { MPI_Recv(val_a_loc.data(), static_cast(local_nnz), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Recv(col_a_loc.data(), static_cast(local_nnz), MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); @@ -96,6 +134,11 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< const std::vector &val_b, const std::vector &col_b, const std::vector &ptr_b, std::vector &res_loc, int local_rows, size_t cols, const std::vector &ptr_a, int start_row) { + // Проверка границ + if (local_rows <= 0 || cols == 0) { + return; + } + for (int i = 0; i < local_rows; ++i) { size_t row_start_offset = ptr_a[start_row + i] - ptr_a[start_row]; size_t row_end_offset = ptr_a[start_row + i + 1] - ptr_a[start_row]; @@ -104,9 +147,25 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< double a_val = val_a_loc[k]; size_t a_col = col_a_loc[k]; + // Проверка границ для матрицы B + if (a_col >= ptr_b.size() - 1) { + continue; // Индекс вне границ + } + for (size_t j = ptr_b[a_col]; j < ptr_b[a_col + 1]; ++j) { - size_t index = (static_cast(i) * cols) + col_b[j]; - res_loc[index] += a_val * val_b[j]; + if (j >= col_b.size()) { + continue; // Индекс вне границ + } + + size_t col_b_idx = col_b[j]; + if (col_b_idx >= cols) { + continue; // Индекс вне границ + } + + size_t index = static_cast(i) * cols + col_b_idx; + if (index < res_loc.size()) { + res_loc[index] += a_val * val_b[j]; + } } } } @@ -117,25 +176,35 @@ void GatherResults(std::vector &full_res, const std::vector &res std::vector counts(size); std::vector displs(size); int send_cnt = local_rows * static_cast(cols); + + // Проверка на переполнение + if (send_cnt < 0) { + send_cnt = 0; + } + MPI_Gather(&send_cnt, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); if (rank == 0) { - full_res.resize(rows * cols); + size_t total_elements = rows * cols; + // Проверка на разумный размер + if (total_elements > 10000000) { // 10 млн элементов макс + throw std::runtime_error("Matrix too large for MPI broadcast"); + } + full_res.resize(total_elements, 0.0); + displs[0] = 0; for (int i = 1; i < size; ++i) { displs[i] = displs[i - 1] + counts[i - 1]; } } - MPI_Gatherv(res_loc.data(), send_cnt, MPI_DOUBLE, full_res.data(), counts.data(), displs.data(), MPI_DOUBLE, 0, - MPI_COMM_WORLD); + // Все процессы должны иметь output правильного размера для Bcast + output.resize(rows * cols); - if (rank == 0) { - output = std::move(full_res); - } else { - output.resize(rows * cols); - } + MPI_Gatherv(res_loc.data(), send_cnt, MPI_DOUBLE, rank == 0 ? output.data() : nullptr, counts.data(), displs.data(), + MPI_DOUBLE, 0, MPI_COMM_WORLD); + // Рассылаем результат всем процессам MPI_Bcast(output.data(), static_cast(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); } @@ -178,6 +247,11 @@ bool FatehovKMatrixCRSMPI::RunImpl() { size_t cols = 0; BroadcastMatrixSizes(rows, cols, rank, input); + // Проверка на допустимые размеры + if (rows == 0 || cols == 0 || rows > 10000 || cols > 10000) { + return false; + } + std::vector val_b{}; std::vector col_b{}; std::vector ptr_b{}; @@ -192,6 +266,13 @@ bool FatehovKMatrixCRSMPI::RunImpl() { int end_row = 0; DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); + // Дополнительная проверка границ + if (start_row < 0 || end_row > static_cast(rows) || start_row >= end_row) { + local_rows = 0; + start_row = 0; + end_row = 0; + } + std::vector val_a_loc{}; std::vector col_a_loc{}; ScatterMatrixA(val_a_loc, col_a_loc, ptr_a, start_row, end_row, rank, size, input); From fd73e4f1a496d69d62bbf707c7452330a9b84bd2 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 4 Jan 2026 00:33:42 +0300 Subject: [PATCH 08/12] fixes_2.0 --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 0cf381c6d7..d35e3274f3 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -18,8 +18,8 @@ void BroadcastMatrixSizes(size_t &rows, size_t &cols, int rank, const InType &in rows = std::get<0>(input); cols = std::get<1>(input); } - MPI_Bcast(&rows, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - MPI_Bcast(&cols, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(&rows, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(&cols, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } void BroadcastMatrixB(std::vector &val_b, std::vector &col_b, std::vector &ptr_b, size_t &nnz_b, @@ -30,17 +30,20 @@ void BroadcastMatrixB(std::vector &val_b, std::vector &col_b, st ptr_b = std::get<7>(input); nnz_b = val_b.size(); } - MPI_Bcast(&nnz_b, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(&nnz_b, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + if (rank != 0) { val_b.resize(nnz_b); col_b.resize(nnz_b); } + if (nnz_b > 0) { MPI_Bcast(val_b.data(), static_cast(nnz_b), MPI_DOUBLE, 0, MPI_COMM_WORLD); - MPI_Bcast(col_b.data(), static_cast(nnz_b), MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(col_b.data(), static_cast(nnz_b), MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } + ptr_b.resize(rows + 1); - MPI_Bcast(ptr_b.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(ptr_b.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank, const InType &input) { @@ -48,28 +51,23 @@ void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank if (rank == 0) { ptr_a = std::get<6>(input); } - MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } -// ИСПРАВЛЕННАЯ функция! void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t rows, int size, int rank) { int rows_per_proc = static_cast(rows) / size; int rem = static_cast(rows) % size; - // Количество строк для текущего процесса local_rows = rows_per_proc + (rank < rem ? 1 : 0); - // Начальная строка - сумма строк всех предыдущих процессов start_row = 0; for (int i = 0; i < rank; ++i) { int i_rows = rows_per_proc + (i < rem ? 1 : 0); start_row += i_rows; } - // Конечная строка (не включая) end_row = start_row + local_rows; - // Проверка границ if (end_row > static_cast(rows)) { end_row = static_cast(rows); } @@ -80,7 +78,6 @@ void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t r void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_loc, const std::vector &ptr_a, int start_row, int end_row, int rank, int size, const InType &input) { - // Проверка границ if (start_row < 0 || end_row > static_cast(ptr_a.size() - 1)) { val_a_loc.clear(); col_a_loc.clear(); @@ -96,7 +93,6 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l const auto &cols_a = std::get<4>(input); for (int i = 1; i < size; ++i) { - // Пересчитываем для каждого процесса int i_rows_per_proc = static_cast(ptr_a.size() - 1) / size; int i_rem = static_cast(ptr_a.size() - 1) % size; int i_local_rows = i_rows_per_proc + (i < i_rem ? 1 : 0); @@ -114,7 +110,7 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l size_t sz = ptr_a[i_end_row] - ptr_a[i_start_row]; if (sz > 0) { MPI_Send(&values_a[ptr_a[i_start_row]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); - MPI_Send(&cols_a[ptr_a[i_start_row]], static_cast(sz), MPI_UNSIGNED_LONG, i, 1, MPI_COMM_WORLD); + MPI_Send(&cols_a[ptr_a[i_start_row]], static_cast(sz), MPI_UNSIGNED_LONG_LONG, i, 1, MPI_COMM_WORLD); } } @@ -126,7 +122,8 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l } } else if (local_nnz > 0) { MPI_Recv(val_a_loc.data(), static_cast(local_nnz), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - MPI_Recv(col_a_loc.data(), static_cast(local_nnz), MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + MPI_Recv(col_a_loc.data(), static_cast(local_nnz), MPI_UNSIGNED_LONG_LONG, 0, 1, MPI_COMM_WORLD, + MPI_STATUS_IGNORE); } } @@ -134,7 +131,6 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< const std::vector &val_b, const std::vector &col_b, const std::vector &ptr_b, std::vector &res_loc, int local_rows, size_t cols, const std::vector &ptr_a, int start_row) { - // Проверка границ if (local_rows <= 0 || cols == 0) { return; } @@ -147,19 +143,18 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< double a_val = val_a_loc[k]; size_t a_col = col_a_loc[k]; - // Проверка границ для матрицы B if (a_col >= ptr_b.size() - 1) { - continue; // Индекс вне границ + continue; } for (size_t j = ptr_b[a_col]; j < ptr_b[a_col + 1]; ++j) { if (j >= col_b.size()) { - continue; // Индекс вне границ + continue; } size_t col_b_idx = col_b[j]; if (col_b_idx >= cols) { - continue; // Индекс вне границ + continue; } size_t index = static_cast(i) * cols + col_b_idx; @@ -177,7 +172,6 @@ void GatherResults(std::vector &full_res, const std::vector &res std::vector displs(size); int send_cnt = local_rows * static_cast(cols); - // Проверка на переполнение if (send_cnt < 0) { send_cnt = 0; } @@ -186,8 +180,7 @@ void GatherResults(std::vector &full_res, const std::vector &res if (rank == 0) { size_t total_elements = rows * cols; - // Проверка на разумный размер - if (total_elements > 10000000) { // 10 млн элементов макс + if (total_elements > 10000000) { throw std::runtime_error("Matrix too large for MPI broadcast"); } full_res.resize(total_elements, 0.0); @@ -198,13 +191,11 @@ void GatherResults(std::vector &full_res, const std::vector &res } } - // Все процессы должны иметь output правильного размера для Bcast output.resize(rows * cols); MPI_Gatherv(res_loc.data(), send_cnt, MPI_DOUBLE, rank == 0 ? output.data() : nullptr, counts.data(), displs.data(), MPI_DOUBLE, 0, MPI_COMM_WORLD); - // Рассылаем результат всем процессам MPI_Bcast(output.data(), static_cast(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); } @@ -247,7 +238,6 @@ bool FatehovKMatrixCRSMPI::RunImpl() { size_t cols = 0; BroadcastMatrixSizes(rows, cols, rank, input); - // Проверка на допустимые размеры if (rows == 0 || cols == 0 || rows > 10000 || cols > 10000) { return false; } @@ -266,7 +256,6 @@ bool FatehovKMatrixCRSMPI::RunImpl() { int end_row = 0; DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); - // Дополнительная проверка границ if (start_row < 0 || end_row > static_cast(rows) || start_row >= end_row) { local_rows = 0; start_row = 0; From 5e862ecb2a7abcd9138180438c766f1e276f2aef Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 4 Jan 2026 01:07:03 +0300 Subject: [PATCH 09/12] tidy fixes_2.0 --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 93 +++++++------------ .../tests/performance/main.cpp | 4 +- 2 files changed, 33 insertions(+), 64 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index d35e3274f3..088a679505 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include "fatehov_k_matrix_crs/common/include/common.hpp" @@ -39,11 +39,11 @@ void BroadcastMatrixB(std::vector &val_b, std::vector &col_b, st if (nnz_b > 0) { MPI_Bcast(val_b.data(), static_cast(nnz_b), MPI_DOUBLE, 0, MPI_COMM_WORLD); - MPI_Bcast(col_b.data(), static_cast(nnz_b), MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(static_cast(col_b.data()), static_cast(nnz_b), MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } ptr_b.resize(rows + 1); - MPI_Bcast(ptr_b.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(static_cast(ptr_b.data()), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank, const InType &input) { @@ -51,34 +51,28 @@ void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank if (rank == 0) { ptr_a = std::get<6>(input); } - MPI_Bcast(ptr_a.data(), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(static_cast(ptr_a.data()), static_cast(rows) + 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); } void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t rows, int size, int rank) { - int rows_per_proc = static_cast(rows) / size; - int rem = static_cast(rows) % size; + int rows_total = static_cast(rows); + int rows_per_proc = rows_total / size; + int rem = rows_total % size; local_rows = rows_per_proc + (rank < rem ? 1 : 0); start_row = 0; for (int i = 0; i < rank; ++i) { - int i_rows = rows_per_proc + (i < rem ? 1 : 0); - start_row += i_rows; + start_row += (rows_per_proc + (i < rem ? 1 : 0)); } - end_row = start_row + local_rows; - - if (end_row > static_cast(rows)) { - end_row = static_cast(rows); - } - if (start_row > end_row) { - start_row = end_row; - } + end_row = std::min(start_row + local_rows, rows_total); + start_row = std::min(start_row, end_row); } void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_loc, const std::vector &ptr_a, int start_row, int end_row, int rank, int size, const InType &input) { - if (start_row < 0 || end_row > static_cast(ptr_a.size() - 1)) { + if (start_row < 0 || static_cast(end_row) > ptr_a.size() - 1) { val_a_loc.clear(); col_a_loc.clear(); return; @@ -91,39 +85,31 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l if (rank == 0) { const auto &values_a = std::get<2>(input); const auto &cols_a = std::get<4>(input); + int rows_total = static_cast(ptr_a.size() - 1); for (int i = 1; i < size; ++i) { - int i_rows_per_proc = static_cast(ptr_a.size() - 1) / size; - int i_rem = static_cast(ptr_a.size() - 1) % size; - int i_local_rows = i_rows_per_proc + (i < i_rem ? 1 : 0); - int i_start_row = 0; + int i_start = 0; for (int j = 0; j < i; ++j) { - int j_rows = i_rows_per_proc + (j < i_rem ? 1 : 0); - i_start_row += j_rows; - } - int i_end_row = i_start_row + i_local_rows; - - if (i_end_row > static_cast(ptr_a.size() - 1)) { - i_end_row = static_cast(ptr_a.size() - 1); + i_start += (rows_total / size + (j < (rows_total % size) ? 1 : 0)); } + int i_end = std::min(i_start + (rows_total / size + (i < (rows_total % size) ? 1 : 0)), rows_total); - size_t sz = ptr_a[i_end_row] - ptr_a[i_start_row]; + size_t sz = ptr_a[i_end] - ptr_a[i_start]; if (sz > 0) { - MPI_Send(&values_a[ptr_a[i_start_row]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); - MPI_Send(&cols_a[ptr_a[i_start_row]], static_cast(sz), MPI_UNSIGNED_LONG_LONG, i, 1, MPI_COMM_WORLD); + MPI_Send(&values_a[ptr_a[i_start]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); + MPI_Send(static_cast(&cols_a[ptr_a[i_start]]), static_cast(sz), MPI_UNSIGNED_LONG_LONG, i, 1, + MPI_COMM_WORLD); } } if (local_nnz > 0) { - std::copy(values_a.begin() + static_cast(ptr_a[start_row]), - values_a.begin() + static_cast(ptr_a[end_row]), val_a_loc.begin()); - std::copy(cols_a.begin() + static_cast(ptr_a[start_row]), - cols_a.begin() + static_cast(ptr_a[end_row]), col_a_loc.begin()); + std::copy(values_a.begin() + ptr_a[start_row], values_a.begin() + ptr_a[end_row], val_a_loc.begin()); + std::copy(cols_a.begin() + ptr_a[start_row], cols_a.begin() + ptr_a[end_row], col_a_loc.begin()); } } else if (local_nnz > 0) { MPI_Recv(val_a_loc.data(), static_cast(local_nnz), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - MPI_Recv(col_a_loc.data(), static_cast(local_nnz), MPI_UNSIGNED_LONG_LONG, 0, 1, MPI_COMM_WORLD, - MPI_STATUS_IGNORE); + MPI_Recv(static_cast(col_a_loc.data()), static_cast(local_nnz), MPI_UNSIGNED_LONG_LONG, 0, 1, + MPI_COMM_WORLD, MPI_STATUS_IGNORE); } } @@ -136,10 +122,10 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< } for (int i = 0; i < local_rows; ++i) { - size_t row_start_offset = ptr_a[start_row + i] - ptr_a[start_row]; - size_t row_end_offset = ptr_a[start_row + i + 1] - ptr_a[start_row]; + size_t row_start = ptr_a[start_row + i] - ptr_a[start_row]; + size_t row_end = ptr_a[start_row + i + 1] - ptr_a[start_row]; - for (size_t k = row_start_offset; k < row_end_offset; ++k) { + for (size_t k = row_start; k < row_end; ++k) { double a_val = val_a_loc[k]; size_t a_col = col_a_loc[k]; @@ -148,16 +134,8 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< } for (size_t j = ptr_b[a_col]; j < ptr_b[a_col + 1]; ++j) { - if (j >= col_b.size()) { - continue; - } - size_t col_b_idx = col_b[j]; - if (col_b_idx >= cols) { - continue; - } - - size_t index = static_cast(i) * cols + col_b_idx; + size_t index = (static_cast(i) * cols) + col_b_idx; if (index < res_loc.size()) { res_loc[index] += a_val * val_b[j]; } @@ -170,21 +148,15 @@ void GatherResults(std::vector &full_res, const std::vector &res size_t cols, int rank, int size, OutType &output) { std::vector counts(size); std::vector displs(size); - int send_cnt = local_rows * static_cast(cols); - - if (send_cnt < 0) { - send_cnt = 0; - } + int send_cnt = std::max(local_rows * static_cast(cols), 0); MPI_Gather(&send_cnt, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); if (rank == 0) { - size_t total_elements = rows * cols; - if (total_elements > 10000000) { + if (rows * cols > 10000000) { throw std::runtime_error("Matrix too large for MPI broadcast"); } - full_res.resize(total_elements, 0.0); - + full_res.resize(rows * cols, 0.0); displs[0] = 0; for (int i = 1; i < size; ++i) { displs[i] = displs[i - 1] + counts[i - 1]; @@ -192,10 +164,8 @@ void GatherResults(std::vector &full_res, const std::vector &res } output.resize(rows * cols); - MPI_Gatherv(res_loc.data(), send_cnt, MPI_DOUBLE, rank == 0 ? output.data() : nullptr, counts.data(), displs.data(), MPI_DOUBLE, 0, MPI_COMM_WORLD); - MPI_Bcast(output.data(), static_cast(rows * cols), MPI_DOUBLE, 0, MPI_COMM_WORLD); } @@ -233,7 +203,6 @@ bool FatehovKMatrixCRSMPI::RunImpl() { MPI_Comm_size(MPI_COMM_WORLD, &size); const auto &input = GetInput(); - size_t rows = 0; size_t cols = 0; BroadcastMatrixSizes(rows, cols, rank, input); @@ -256,7 +225,7 @@ bool FatehovKMatrixCRSMPI::RunImpl() { int end_row = 0; DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); - if (start_row < 0 || end_row > static_cast(rows) || start_row >= end_row) { + if (start_row < 0 || static_cast(end_row) > rows || start_row >= end_row) { local_rows = 0; start_row = 0; end_row = 0; diff --git a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp index f235fe517d..972159ec51 100644 --- a/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp +++ b/tasks/fatehov_k_matrix_crs/tests/performance/main.cpp @@ -19,8 +19,8 @@ class FatehovKRunPerfTestsMatrixCRS : public ppc::util::BaseRunPerfTests values{}; From 03ed8bcdd152eed67864475b87e858af8e34e4c4 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 4 Jan 2026 01:40:42 +0300 Subject: [PATCH 10/12] tidy fixes_3.0 --- .../fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 83 ++++++++++--------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 088a679505..1b36979203 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "fatehov_k_matrix_crs/common/include/common.hpp" @@ -13,6 +14,18 @@ namespace fatehov_k_matrix_crs { namespace { +// Вспомогательная функция для вычисления границ строк для конкретного процесса +std::pair GetProcessRowRange(int target_rank, int size, int rows_total) { + int rows_per_proc = rows_total / size; + int rem = rows_total % size; + int start = 0; + for (int j = 0; j < target_rank; ++j) { + start += (rows_per_proc + (j < rem ? 1 : 0)); + } + int end = start + (rows_per_proc + (target_rank < rem ? 1 : 0)); + return {start, std::min(end, rows_total)}; +} + void BroadcastMatrixSizes(size_t &rows, size_t &cols, int rank, const InType &input) { if (rank == 0) { rows = std::get<0>(input); @@ -55,24 +68,32 @@ void BroadcastMatrixAStructure(std::vector &ptr_a, size_t rows, int rank } void DistributeLocalWork(int &local_rows, int &start_row, int &end_row, size_t rows, int size, int rank) { - int rows_total = static_cast(rows); - int rows_per_proc = rows_total / size; - int rem = rows_total % size; - - local_rows = rows_per_proc + (rank < rem ? 1 : 0); + auto range = GetProcessRowRange(rank, size, static_cast(rows)); + start_row = range.first; + end_row = range.second; + local_rows = end_row - start_row; +} - start_row = 0; - for (int i = 0; i < rank; ++i) { - start_row += (rows_per_proc + (i < rem ? 1 : 0)); +// Логика отправки данных от корня (rank 0) другим процессам +void SendMatrixAParts(int size, const std::vector &ptr_a, const InType &input) { + const auto &values_a = std::get<2>(input); + const auto &cols_a = std::get<4>(input); + int rows_total = static_cast(ptr_a.size() - 1); + + for (int i = 1; i < size; ++i) { + auto range = GetProcessRowRange(i, size, rows_total); + size_t sz = ptr_a[range.second] - ptr_a[range.first]; + if (sz > 0) { + MPI_Send(&values_a[ptr_a[range.first]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); + MPI_Send(static_cast(&cols_a[ptr_a[range.first]]), static_cast(sz), MPI_UNSIGNED_LONG_LONG, i, + 1, MPI_COMM_WORLD); + } } - - end_row = std::min(start_row + local_rows, rows_total); - start_row = std::min(start_row, end_row); } void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_loc, const std::vector &ptr_a, int start_row, int end_row, int rank, int size, const InType &input) { - if (start_row < 0 || static_cast(end_row) > ptr_a.size() - 1) { + if (start_row < 0 || std::cmp_greater(end_row, ptr_a.size() - 1)) { val_a_loc.clear(); col_a_loc.clear(); return; @@ -83,28 +104,14 @@ void ScatterMatrixA(std::vector &val_a_loc, std::vector &col_a_l col_a_loc.resize(local_nnz); if (rank == 0) { - const auto &values_a = std::get<2>(input); - const auto &cols_a = std::get<4>(input); - int rows_total = static_cast(ptr_a.size() - 1); - - for (int i = 1; i < size; ++i) { - int i_start = 0; - for (int j = 0; j < i; ++j) { - i_start += (rows_total / size + (j < (rows_total % size) ? 1 : 0)); - } - int i_end = std::min(i_start + (rows_total / size + (i < (rows_total % size) ? 1 : 0)), rows_total); - - size_t sz = ptr_a[i_end] - ptr_a[i_start]; - if (sz > 0) { - MPI_Send(&values_a[ptr_a[i_start]], static_cast(sz), MPI_DOUBLE, i, 0, MPI_COMM_WORLD); - MPI_Send(static_cast(&cols_a[ptr_a[i_start]]), static_cast(sz), MPI_UNSIGNED_LONG_LONG, i, 1, - MPI_COMM_WORLD); - } - } - + SendMatrixAParts(size, ptr_a, input); if (local_nnz > 0) { - std::copy(values_a.begin() + ptr_a[start_row], values_a.begin() + ptr_a[end_row], val_a_loc.begin()); - std::copy(cols_a.begin() + ptr_a[start_row], cols_a.begin() + ptr_a[end_row], col_a_loc.begin()); + const auto &v_a = std::get<2>(input); + const auto &c_a = std::get<4>(input); + std::copy(v_a.begin() + static_cast(ptr_a[start_row]), + v_a.begin() + static_cast(ptr_a[end_row]), val_a_loc.begin()); + std::copy(c_a.begin() + static_cast(ptr_a[start_row]), + c_a.begin() + static_cast(ptr_a[end_row]), col_a_loc.begin()); } } else if (local_nnz > 0) { MPI_Recv(val_a_loc.data(), static_cast(local_nnz), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); @@ -122,13 +129,13 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< } for (int i = 0; i < local_rows; ++i) { - size_t row_start = ptr_a[start_row + i] - ptr_a[start_row]; - size_t row_end = ptr_a[start_row + i + 1] - ptr_a[start_row]; + size_t row_idx = static_cast(start_row + i); + size_t row_start = ptr_a[row_idx] - ptr_a[start_row]; + size_t row_end = ptr_a[row_idx + 1] - ptr_a[start_row]; for (size_t k = row_start; k < row_end; ++k) { double a_val = val_a_loc[k]; size_t a_col = col_a_loc[k]; - if (a_col >= ptr_b.size() - 1) { continue; } @@ -153,7 +160,7 @@ void GatherResults(std::vector &full_res, const std::vector &res MPI_Gather(&send_cnt, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD); if (rank == 0) { - if (rows * cols > 10000000) { + if (std::cmp_greater(rows * cols, 10000000)) { throw std::runtime_error("Matrix too large for MPI broadcast"); } full_res.resize(rows * cols, 0.0); @@ -225,7 +232,7 @@ bool FatehovKMatrixCRSMPI::RunImpl() { int end_row = 0; DistributeLocalWork(local_rows, start_row, end_row, rows, size, rank); - if (start_row < 0 || static_cast(end_row) > rows || start_row >= end_row) { + if (start_row < 0 || std::cmp_greater(end_row, rows) || start_row >= end_row) { local_rows = 0; start_row = 0; end_row = 0; From 2bc72e8ac670f1929390f863a674d9d4e60f26f8 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 4 Jan 2026 02:15:21 +0300 Subject: [PATCH 11/12] tidy fixes_4.0 --- tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp | 2 +- tasks/fatehov_k_matrix_crs/report.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp index 1b36979203..c73a488e5a 100644 --- a/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp +++ b/tasks/fatehov_k_matrix_crs/mpi/src/ops_mpi.cpp @@ -129,7 +129,7 @@ void ComputeLocalResult(const std::vector &val_a_loc, const std::vector< } for (int i = 0; i < local_rows; ++i) { - size_t row_idx = static_cast(start_row + i); + size_t row_idx = static_cast(start_row) + static_cast(i); size_t row_start = ptr_a[row_idx] - ptr_a[start_row]; size_t row_end = ptr_a[row_idx + 1] - ptr_a[start_row]; diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md index d5b6da0143..bfd22b6c41 100644 --- a/tasks/fatehov_k_matrix_crs/report.md +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -176,11 +176,11 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); ``` **Параметры генерации для тестов производительности:** -- Размер матриц: `100×100` +- Размер матриц: `1000×1000` - Разреженность: `2%` -- Примерное количество ненулевых элементов: `100×100×0.02` = `10000` на матрицу +- Примерное количество ненулевых элементов: `1000×1000×0.02` = `1000000` на матрицу ## 6. Экспериментальная среда - Hardware/OS: AMD RYZEN 5 5600 6-Core Processor, 12-Threads, 16GB, Ubuntu (DevContainer/WSL 2.0) From 2ad42f23c6d653b886f09081609ba982c513c7b5 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sun, 4 Jan 2026 03:57:42 +0300 Subject: [PATCH 12/12] report_fix_2.0 --- tasks/fatehov_k_matrix_crs/report.md | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/tasks/fatehov_k_matrix_crs/report.md b/tasks/fatehov_k_matrix_crs/report.md index bfd22b6c41..2602a98e65 100644 --- a/tasks/fatehov_k_matrix_crs/report.md +++ b/tasks/fatehov_k_matrix_crs/report.md @@ -189,29 +189,7 @@ GatherResults(full_res, res_loc, local_rows, rows, cols, rank, size, output); ## 7. Результаты и обсуждение -### 7.1 Корректность -**Модульные тесты** - -Реализовано 3 тестовых случая: - -- Матрицы 2×2 (единичная матрица × произвольная) - -- Матрицы 2×2 с умножением на скаляр - -- Матрицы 2×2 со смешанными значениями - -**Условие корректности:** -- SEQ и MPI версии дают идентичные результаты с точностью до `1e-10`. - - **Результаты тестирования:** - -- Все тестовые случаи успешно пройдены - -- SEQ и MPI реализации выдают идентичные результаты - -- Обработка граничных случаев корректна - -### 7.2 Производительность +### 7.1 Производительность | **Режим** | **Количество процессов** | **Время, с** | **Ускорение** | **Эффективность** |