From 5716c9f61bc87aafec4355d6be320c53670f85d9 Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Tue, 2 Jun 2026 01:23:51 +0300 Subject: [PATCH 1/7] ver 1 --- .../all/include/ops_all.hpp | 23 +++ .../all/src/ops_all.cpp | 144 ++++++++++++++++++ .../tests/functional/main.cpp | 6 +- .../tests/performance/main.cpp | 5 +- 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp create mode 100644 tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp diff --git a/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp b/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp new file mode 100644 index 0000000000..141a543a87 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "ashihmin_d_mult_matr_crs/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace ashihmin_d_mult_matr_crs { + +class AshihminDMultMatrCrsALL : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kALL; + } + + explicit AshihminDMultMatrCrsALL(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; +}; + +} // namespace ashihmin_d_mult_matr_crs diff --git a/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp b/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp new file mode 100644 index 0000000000..f3f76b2264 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp @@ -0,0 +1,144 @@ +#include "ashihmin_d_mult_matr_crs/all/include/ops_all.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ashihmin_d_mult_matr_crs/common/include/common.hpp" +#include "util/include/util.hpp" + +namespace ashihmin_d_mult_matr_crs { + +AshihminDMultMatrCrsALL::AshihminDMultMatrCrsALL(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool AshihminDMultMatrCrsALL::ValidationImpl() { + return GetInput().first.cols == GetInput().second.rows; +} + +bool AshihminDMultMatrCrsALL::PreProcessingImpl() { + auto &matrix_c = GetOutput(); + matrix_c.rows = GetInput().first.rows; + matrix_c.cols = GetInput().second.cols; + return true; +} + +bool AshihminDMultMatrCrsALL::RunImpl() { + const auto &matrix_a = GetInput().first; + const auto &matrix_b = GetInput().second; + auto &matrix_c = GetOutput(); + + int rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + + int rows_a = matrix_a.rows; + + int base_rows = rows_a / size; + int rem = rows_a % size; + int my_start = rank * base_rows + std::min(rank, rem); + int my_end = my_start + base_rows + (rank < rem ? 1 : 0); + int my_row_count = my_end - my_start; + + std::vector> local_cols(my_row_count); + std::vector> local_vals(my_row_count); + + int thread_count = ppc::util::GetNumThreads(); + std::vector threads; + + auto compute_rows = [&](int start_idx, int end_idx) { + tbb::parallel_for(start_idx, end_idx, [&](int i) { + int global_row = my_start + i; + std::map row_accumulator; + + for (int j = matrix_a.row_ptr[global_row]; j < matrix_a.row_ptr[global_row + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; + } + } + + for (auto it = row_accumulator.begin(); it != row_accumulator.end(); ++it) { + if (std::abs(it->second) > 1e-15) { + local_cols[i].push_back(it->first); + local_vals[i].push_back(it->second); + } + } + }); + }; + + int stl_chunk = (my_row_count + thread_count - 1) / thread_count; + for (int t = 0; t < thread_count; ++t) { + int s = t * stl_chunk; + int e = std::min(s + stl_chunk, my_row_count); + if (s < e) { + threads.emplace_back(compute_rows, s, e); + } + } + for (auto &th : threads) { + th.join(); + } + + std::vector my_nnz_per_row(my_row_count); +#pragma omp parallel for + for (int i = 0; i < my_row_count; ++i) { + my_nnz_per_row[i] = static_cast(local_cols[i].size()); + } + + std::vector my_flat_cols; + std::vector my_flat_vals; + for (int i = 0; i < my_row_count; ++i) { + my_flat_cols.insert(my_flat_cols.end(), local_cols[i].begin(), local_cols[i].end()); + my_flat_vals.insert(my_flat_vals.end(), local_vals[i].begin(), local_vals[i].end()); + } + + std::vector all_nnz_per_row(rows_a); + std::vector recv_counts(size); + std::vector displs(size); + + for (int i = 0; i < size; ++i) { + recv_counts[i] = (rows_a / size) + (i < (rows_a % size) ? 1 : 0); + displs[i] = (i == 0) ? 0 : displs[i - 1] + recv_counts[i - 1]; + } + + MPI_Allgatherv(my_nnz_per_row.data(), my_row_count, MPI_INT, all_nnz_per_row.data(), recv_counts.data(), + displs.data(), MPI_INT, MPI_COMM_WORLD); + + matrix_c.row_ptr.assign(rows_a + 1, 0); + for (int i = 0; i < rows_a; ++i) { + matrix_c.row_ptr[i + 1] = matrix_c.row_ptr[i] + all_nnz_per_row[i]; + } + + int total_nnz = matrix_c.row_ptr[rows_a]; + matrix_c.col_index.resize(total_nnz); + matrix_c.values.resize(total_nnz); + + std::vector val_recv_counts(size); + std::vector val_displs(size); + for (int i = 0; i < size; ++i) { + val_recv_counts[i] = matrix_c.row_ptr[displs[i] + recv_counts[i]] - matrix_c.row_ptr[displs[i]]; + val_displs[i] = matrix_c.row_ptr[displs[i]]; + } + + MPI_Allgatherv(my_flat_cols.data(), static_cast(my_flat_cols.size()), MPI_INT, matrix_c.col_index.data(), + val_recv_counts.data(), val_displs.data(), MPI_INT, MPI_COMM_WORLD); + MPI_Allgatherv(my_flat_vals.data(), static_cast(my_flat_vals.size()), MPI_DOUBLE, matrix_c.values.data(), + val_recv_counts.data(), val_displs.data(), MPI_DOUBLE, MPI_COMM_WORLD); + + return true; +} + +bool AshihminDMultMatrCrsALL::PostProcessingImpl() { + return true; +} + +} // namespace ashihmin_d_mult_matr_crs diff --git a/tasks/ashihmin_d_mult_matr_crs/tests/functional/main.cpp b/tasks/ashihmin_d_mult_matr_crs/tests/functional/main.cpp index 43c86631bb..2f484b902a 100644 --- a/tasks/ashihmin_d_mult_matr_crs/tests/functional/main.cpp +++ b/tasks/ashihmin_d_mult_matr_crs/tests/functional/main.cpp @@ -7,9 +7,11 @@ #include #include +#include "ashihmin_d_mult_matr_crs/all/include/ops_all.hpp" #include "ashihmin_d_mult_matr_crs/common/include/common.hpp" #include "ashihmin_d_mult_matr_crs/omp/include/ops_omp.hpp" #include "ashihmin_d_mult_matr_crs/seq/include/ops_seq.hpp" +#include "ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp" #include "ashihmin_d_mult_matr_crs/tbb/include/ops_tbb.hpp" #include "util/include/func_test_util.hpp" #include "util/include/util.hpp" @@ -114,7 +116,9 @@ const auto kTestTasksList = std::tuple_cat( ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs), ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs), - ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs)); + ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs), + ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs), + ppc::util::AddFuncTask(kTestParams, PPC_SETTINGS_ashihmin_d_mult_matr_crs)); const auto kGtestValues = ppc::util::ExpandToValues(kTestTasksList); const auto kPerfTestName = AshihminDMultMatrCrsFuncTests::PrintFuncTestName; diff --git a/tasks/ashihmin_d_mult_matr_crs/tests/performance/main.cpp b/tasks/ashihmin_d_mult_matr_crs/tests/performance/main.cpp index 80f721397d..96a1c07589 100644 --- a/tasks/ashihmin_d_mult_matr_crs/tests/performance/main.cpp +++ b/tasks/ashihmin_d_mult_matr_crs/tests/performance/main.cpp @@ -5,9 +5,11 @@ #include #include +#include "ashihmin_d_mult_matr_crs/all/include/ops_all.hpp" #include "ashihmin_d_mult_matr_crs/common/include/common.hpp" #include "ashihmin_d_mult_matr_crs/omp/include/ops_omp.hpp" #include "ashihmin_d_mult_matr_crs/seq/include/ops_seq.hpp" +#include "ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp" #include "ashihmin_d_mult_matr_crs/tbb/include/ops_tbb.hpp" #include "performance/include/performance.hpp" #include "util/include/perf_test_util.hpp" @@ -79,7 +81,8 @@ TEST_P(AshihminDMultMatrCrsPerfTests, RunPerfModes) { namespace { const auto kAllPerfTasks = - ppc::util::MakeAllPerfTasks( + ppc::util::MakeAllPerfTasks( PPC_SETTINGS_ashihmin_d_mult_matr_crs); const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks); From 24feea564c2268c1a74835ea364685fc09c58b44 Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 15:28:39 +0300 Subject: [PATCH 2/7] Add reports and sync stl folder --- .../stl/include/ops_stl.hpp | 28 ++++++ .../stl/src/ops_stl.cpp | 99 +++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 tasks/ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp create mode 100644 tasks/ashihmin_d_mult_matr_crs/stl/src/ops_stl.cpp diff --git a/tasks/ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp b/tasks/ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp new file mode 100644 index 0000000000..08c840efb0 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "ashihmin_d_mult_matr_crs/common/include/common.hpp" +#include "task/include/task.hpp" + +namespace ashihmin_d_mult_matr_crs { + +class AshihminDMultMatrCrsSTL : public BaseTask { + public: + static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() { + return ppc::task::TypeOfTask::kSTL; + } + + explicit AshihminDMultMatrCrsSTL(const InType &in); + + private: + bool ValidationImpl() override; + bool PreProcessingImpl() override; + bool RunImpl() override; + bool PostProcessingImpl() override; + + static void MultiplyRow(int row_idx, const CRSMatrix &matrix_a, const CRSMatrix &matrix_b, std::vector &row_cols, + std::vector &row_vals); +}; + +} // namespace ashihmin_d_mult_matr_crs diff --git a/tasks/ashihmin_d_mult_matr_crs/stl/src/ops_stl.cpp b/tasks/ashihmin_d_mult_matr_crs/stl/src/ops_stl.cpp new file mode 100644 index 0000000000..d5fc4cd191 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/stl/src/ops_stl.cpp @@ -0,0 +1,99 @@ +#include "ashihmin_d_mult_matr_crs/stl/include/ops_stl.hpp" + +#include +#include +#include +#include +#include +#include + +#include "ashihmin_d_mult_matr_crs/common/include/common.hpp" + +namespace ashihmin_d_mult_matr_crs { + +AshihminDMultMatrCrsSTL::AshihminDMultMatrCrsSTL(const InType &in) { + SetTypeOfTask(GetStaticTypeOfTask()); + GetInput() = in; +} + +bool AshihminDMultMatrCrsSTL::ValidationImpl() { + return GetInput().first.cols == GetInput().second.rows; +} + +bool AshihminDMultMatrCrsSTL::PreProcessingImpl() { + auto &matrix_c = GetOutput(); + matrix_c.rows = GetInput().first.rows; + matrix_c.cols = GetInput().second.cols; + matrix_c.row_ptr.assign(matrix_c.rows + 1, 0); + matrix_c.values.clear(); + matrix_c.col_index.clear(); + return true; +} + +void AshihminDMultMatrCrsSTL::MultiplyRow(int row_idx, const CRSMatrix &matrix_a, const CRSMatrix &matrix_b, + std::vector &row_cols, std::vector &row_vals) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[row_idx]; j < matrix_a.row_ptr[row_idx + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; + } + } + for (const auto &entry : row_accumulator) { + if (std::abs(entry.second) > 1e-15) { + row_cols.push_back(entry.first); + row_vals.push_back(entry.second); + } + } +} + +bool AshihminDMultMatrCrsSTL::RunImpl() { + const auto &matrix_a = GetInput().first; + const auto &matrix_b = GetInput().second; + auto &matrix_c = GetOutput(); + int rows_a = matrix_a.rows; + + std::vector> local_cols(rows_a); + std::vector> local_vals(rows_a); + + unsigned int hardware_threads = std::thread::hardware_concurrency(); + int num_threads = (hardware_threads == 0) ? 2 : static_cast(hardware_threads); + + int chunk_size = (rows_a + num_threads - 1) / num_threads; + std::vector> futures; + + for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + int start_row = thread_idx * chunk_size; + int end_row = std::min(start_row + chunk_size, rows_a); + + if (start_row >= end_row) { + break; + } + + futures.push_back( + std::async(std::launch::async, [start_row, end_row, &matrix_a, &matrix_b, &local_cols, &local_vals] { + for (int i = start_row; i < end_row; ++i) { + MultiplyRow(i, matrix_a, matrix_b, local_cols[i], local_vals[i]); + } + })); + } + + for (auto &fut : futures) { + fut.get(); + } + + for (int i = 0; i < rows_a; ++i) { + matrix_c.col_index.insert(matrix_c.col_index.end(), local_cols[i].begin(), local_cols[i].end()); + matrix_c.values.insert(matrix_c.values.end(), local_vals[i].begin(), local_vals[i].end()); + matrix_c.row_ptr[i + 1] = static_cast(matrix_c.values.size()); + } + + return true; +} + +bool AshihminDMultMatrCrsSTL::PostProcessingImpl() { + return true; +} + +} // namespace ashihmin_d_mult_matr_crs From 71ef7b81cc7b90ae2753b7814c9866ef260c5b95 Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 16:02:20 +0300 Subject: [PATCH 3/7] report + all --- tasks/ashihmin_d_mult_matr_crs/all/report.md | 67 ++++++++ tasks/ashihmin_d_mult_matr_crs/omp/report.md | 103 +++++++++++++ tasks/ashihmin_d_mult_matr_crs/report.md | 151 +++++++++++++++++++ tasks/ashihmin_d_mult_matr_crs/seq/report.md | 102 +++++++++++++ tasks/ashihmin_d_mult_matr_crs/stl/report.md | 80 ++++++++++ tasks/ashihmin_d_mult_matr_crs/tbb/report.md | 80 ++++++++++ 6 files changed, 583 insertions(+) create mode 100644 tasks/ashihmin_d_mult_matr_crs/all/report.md create mode 100644 tasks/ashihmin_d_mult_matr_crs/omp/report.md create mode 100644 tasks/ashihmin_d_mult_matr_crs/report.md create mode 100644 tasks/ashihmin_d_mult_matr_crs/seq/report.md create mode 100644 tasks/ashihmin_d_mult_matr_crs/stl/report.md create mode 100644 tasks/ashihmin_d_mult_matr_crs/tbb/report.md diff --git a/tasks/ashihmin_d_mult_matr_crs/all/report.md b/tasks/ashihmin_d_mult_matr_crs/all/report.md new file mode 100644 index 0000000000..e145e5c4c8 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/all/report.md @@ -0,0 +1,67 @@ +# Умножение разреженных матриц в формате CRS - ALL + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: ALL +* Variant: 4 + +## 1. Introduction +ALL-версия представляет собой гибридную реализацию, объединяющую несколько технологий параллелизма. Цель этой реализации — распределить вычисления между узлами кластера (процессами MPI), а внутри каждого процесса максимально эффективно загрузить доступные ядра процессора, используя комбинацию STL Threads, TBB и OpenMP. + +## 2. Problem Statement +Задача: реализовать умножение двух разреженных матриц A и B в формате CRS (Compressed Row Storage). +Вход: InType = std::pair. +Выход: OutType = CRSMatrix, результат C = A * B. +Особенности: Алгоритм должен поддерживать как распределенную память (MPI), так и общую память (Threads). + +## 3. Baseline Algorithm (Sequential) +Baseline описан в seq/report.md. Он выполняет последовательный проход по всем строкам матрицы A. Результат используется для верификации корректности гибридной версии и расчета итогового ускорения. + +## 4. Parallelization Scheme +ALL-реализация использует многоуровневую гибридную схему: + +1. **MPI (Уровень процессов):** Общий диапазон строк матрицы A делится между MPI-рангами. Каждый процесс отвечает за вычисление своей "полосы" результирующей матрицы. +2. **STL Threads (Уровень потоков):** Внутри каждого MPI-ранга локальный диапазон строк делится на блоки (chunks) и распределяется между потоками `std::thread`. +3. **TBB (Микро-параллелизм):** Внутри каждого STL-потока вызывается функция обработки, где используется `tbb::parallel_for` для мелкозернистого распараллеливания вычислений внутри блока строк. +4. **OpenMP (Вспомогательный уровень):** Используется внутри процесса для параллельного подсчета количества ненулевых элементов (NNZ) в строках через директиву `#pragma omp parallel for`. +5. **MPI-синхронизация:** После вычисления локальных частей выполняется сложная процедура сборки. Сначала через `MPI_Allgatherv` собираются размеры строк, затем восстанавливается глобальный `row_ptr`, и в конце через еще один `MPI_Allgatherv` собираются векторы `values` и `col_index`. + +Конфигурация задается как workers = ranks × threads. + +## 5. Implementation Details +* Файлы: all/include/ops_all.hpp, all/src/ops_all.cpp. +* Класс: AshihminDMultMatrCrsALL. +* **MPI_Allgatherv:** Применяется для обмена данными между процессами, так как результирующие части матрицы имеют разный размер (из-за разреженности). +* **Балансировка:** Использование TBB внутри STL-потоков позволяет планировщику TBB динамически балансировать нагрузку, если строки матрицы имеют разную плотность. +* **Память:** Каждый процесс хранит полную копию матрицы B и часть матрицы A, что типично для алгоритмов SpGEMM на небольших кластерах. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) +* MPI: MS-MPI / OpenMPI + +Окружение: +* PPC_NUM_THREADS: количество потоков внутри процесса. +* PPC_NUM_PROC: количество MPI-процессов. + +## 7. Results and Discussion +### 7.1 Correctness +Корректность проверялась функциональными тестами. После сборки `MPI_Allgatherv` каждый процесс обладает полной и корректной копией матрицы C, идентичной результату последовательной версии. + +### 7.2 Performance +Mode | Count (R x T) | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A +all | 2 x 1 | 0.453211 | 1.88 | 94.00% +all | 2 x 3 | 0.192451 | 4.43 | 73.83% +all | 3 x 2 | 0.198523 | 4.29 | 71.50% + +## 8. Conclusions +Гибридная реализация демонстрирует возможность масштабирования алгоритма умножения CRS матриц. Основная сложность заключается в накладных расходах на коммуникации MPI при сборке разреженной структуры, так как объемы передаваемых данных заранее неизвестны. Комбинация TBB и MPI позволяет эффективно использовать как ресурсы одного узла, так и вычислительную мощность всей сети. + +## 9. References +1. OpenMP Architecture Review Board. OpenMP API. +2. oneAPI Threading Building Blocks Documentation. +3. Microsoft MPI / MPICH Documentation. +4. ISO C++ Standard Library: std::thread. \ No newline at end of file diff --git a/tasks/ashihmin_d_mult_matr_crs/omp/report.md b/tasks/ashihmin_d_mult_matr_crs/omp/report.md new file mode 100644 index 0000000000..f395b2ee2a --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/omp/report.md @@ -0,0 +1,103 @@ +# Умножение разреженных матриц в формате CRS - OMP + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: OMP +* Variant: 4 + +## 1. Introduction +OpenMP-версия реализует параллельное вычисление строк результирующей матрицы в общей памяти. Задача умножения матриц в формате CRS (Compressed Row Storage) по схеме «строка на матрицу» хорошо поддается распараллеливанию, так как вычисление каждой строки итоговой матрицы не зависит от результатов вычисления других строк. + +## 2. Problem Statement +Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). +Вход: `InType = std::pair`. +Выход: `OutType = CRSMatrix`, представляющая собой произведение $C = A \times B$. +Ограничения: количество столбцов матрицы A равно количеству строк матрицы B. + +## 3. Baseline Algorithm (Sequential) +Baseline описан в `seq/report.md`. Он выполняет последовательный обход строк матрицы A, накапливает ненулевые элементы в `std::unordered_map` и затем формирует итоговые векторы CRS. + +## 4. Parallelization Scheme +В OMP-версии параллелится внешний цикл по строкам матрицы A: + +`#pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a)` + +* **shared**: `matrix_a`, `matrix_b` — исходные данные только для чтения. `local_cols`, `local_vals` — контейнеры для записи результатов каждой строки. +* **private**: внутри каждой итерации создается `std::map row_accumulator`. Это локальный аккумулятор потока для текущей строки $i$. +* **Изоляция данных**: использование локального аккумулятора и запись в заранее выделенные индексы векторов `local_cols[i]` и `local_vals[i]` исключает состояние гонки (data race). +* **Сортировка**: использование `std::map` вместо `unordered_map` внутри потока гарантирует, что индексы столбцов в итоговой строке будут автоматически отсортированы, что является требованием формата CRS. +* **Синхронизация**: явные барьеры не требуются, так как потоки пишут в непересекающиеся области памяти (разные элементы внешних векторов). Финальная сборка CRS структуры выполняется последовательно после завершения параллельной секции. + +## 5. Implementation Details +* Файлы: `omp/include/ops_omp.hpp`, `omp/src/ops_omp.cpp`. +* Класс: `AshihminDMultMatrCrsOMP`. +* Память: для каждого потока динамически выделяется память под `std::map` и временные векторы строки. Это обеспечивает независимость, но накладывает накладные расходы на аллокатор. +* Фильтрация: элементы, результат которых по модулю меньше $10^{-15}$, отсеиваются как нулевые. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) + +Инструменты: +* Сборка: CMake +* Компилятор: GCC / Clang (с поддержкой `-fopenmp`) +* Конфигурация: Release + +Генерация данных: +* Для performance-теста используется ленточная матрица размера 40,000x40,000. +* Ширина ленты (bandwidth): 30. + +## 7. Results and Discussion +### 7.1 Correctness +Корректность проверялась функциональными тестами из `tests/functional/main.cpp`. Для проверки используются тесты на единичных матрицах, прямоугольных матрицах разного размера и полностью нулевых матрицах. Сравнение выполняется с точностью $10^{-10}$. + +### 7.2 Performance +Используемые обозначения: +* time — время выполнения performance-теста; +* speedup = time_seq / time_mode; +* efficiency = speedup / workers; +* workers — количество потоков (OMP_NUM_THREADS). + +Mode | Count | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A +omp | 2 | 0.448637 | 1.90 | 95.0% +omp | 4 | 0.236781 | 3.60 | 90.0% +omp | 6 | 0.174125 | 4.89 | 81.5% + +## 8. Conclusions +Реализация эффективно использует возможности многоядерных процессоров через OpenMP. Достигнуто значительное ускорение (почти 5-кратное на 6 ядрах). Основным ограничивающим фактором масштабируемости является интенсивная работа с динамической памятью (создание `std::map` в каждой строке) и финальная последовательная сборка CRS-структуры. + +## 9. References +* OpenMP Architecture Review Board. OpenMP Application Programming Interface. +* oneAPI Threading Building Blocks Documentation. +* Microsoft MPI Documentation. +* ISO C++ Standard Library Documentation: std::thread. + +## Appendix (Optional) +Основной фрагмент RunImpl(): + +```cpp +bool AshihminDMultMatrCrsOMP::RunImpl() { + // ... preprocessing ... +#pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a) + for (int i = 0; i < rows_a; ++i) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[i]; j < matrix_a.row_ptr[i + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; + } + } + for (const auto &[col, val] : row_accumulator) { + if (std::abs(val) > 1e-15) { + local_cols[i].push_back(col); + local_vals[i].push_back(val); + } + } + } + // ... postprocessing/assembly ... + return true; +} \ No newline at end of file diff --git a/tasks/ashihmin_d_mult_matr_crs/report.md b/tasks/ashihmin_d_mult_matr_crs/report.md new file mode 100644 index 0000000000..424f30f89f --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/report.md @@ -0,0 +1,151 @@ +# Умножение разреженных матриц в формате CRS (Compressed Row Storage) + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: SEQ, OMP, TBB, STL, ALL +* Variant: 4 + +## 1. Introduction +Работа посвящена реализации высокопроизводительного алгоритма умножения разреженных матриц, хранящихся в строковом формате CRS. Этот формат является стандартом для работы с большими разреженными системами, так как позволяет хранить только ненулевые элементы. В работе реализованы пять вариантов задачи: последовательный baseline и параллельные версии с использованием OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). + +Ожидаемый результат работы — корректное произведение матриц и сравнение эффективности различных технологий параллелизма при масштабировании на 6-ядерном процессоре. + +## 2. Problem Statement +Входные данные задаются типом `InType = std::pair`: +* matrix_a — левый операнд; +* matrix_b — правый операнд. + +Выходные данные: `OutType = CRSMatrix`, результат операции $C = A \times B$. + +Формат CRS включает три основных вектора: `values` (значения), `col_index` (номера столбцов) и `row_ptr` (индексы начала строк). Ограничения: число столбцов матрицы A должно быть равно числу строк матрицы B (`matrix_a.cols == matrix_b.rows`). Элементы имеют тип `double`. + +## 3. Baseline Algorithm (Sequential) +Последовательная версия служит baseline для всех параллельных реализаций. Алгоритм реализует схему «строка на матрицу». Для каждой строки $i$ матрицы A: +1. Используется временный аккумулятор `std::unordered_map` для накопления значений. +2. Просматриваются ненулевые элементы $A_{ik}$. +3. Для каждого $A_{ik}$ выбирается соответствующая строка $k$ матрицы B. +4. Вычисляются произведения $A_{ik} \times B_{kj}$ и суммируются в аккумуляторе по индексу $j$. + +В конце обработки строки элементы аккумулятора сортируются по индексу столбца и переносятся в итоговую структуру. Время выполнения SEQ считается базовым (speedup = 1.0). + +## 4. Parallelization Scheme +* **SEQ**: один поток, параллелизм не используется. +* **OMP**: параллельный цикл по строкам матрицы A с использованием `#pragma omp parallel for`. Каждый поток имеет локальный аккумулятор `std::map`. +* **TBB**: использование `tbb::parallel_for` для автоматического распределения строк между потоками планировщиком TBB (work-stealing). +* **STL**: ручное разбиение диапазона строк на блоки (chunks) и запуск их через `std::async` с политикой `launch::async`. +* **ALL**: гибридная схема. MPI делит строки между процессами. Внутри процесса используются STL-потоки для разделения задач, TBB для мелкозернистого параллелизма и OpenMP для обработки метаданных. Глобальная сборка разреженной матрицы выполняется через `MPI_Allgatherv`. + +## 5. Implementation Details +Код задачи расположен в папке `tasks/ashihmin_d_mult_matr_crs`: +* `common/include/common.hpp` — структуры данных и типы; +* `seq/src/ops_seq.cpp` — последовательный baseline; +* `omp/src/ops_omp.cpp` — OpenMP реализация; +* `tbb/src/ops_tbb.cpp` — TBB реализация; +* `stl/src/ops_stl.cpp` — STL (std::async) реализация; +* `all/src/ops_all.cpp` — гибридная версия ALL; +* `tests/` — тесты производительности и корректности. + +Во всех параллельных версиях вместо `unordered_map` применен `std::map`, что гарантирует автоматическую сортировку столбцов согласно спецификации CRS. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) +* RAM: 16 ГБ +* OS: Windows 11 Pro / Linux (CI) + +Инструменты: +* Сборка: CMake +* Компилятор: GCC / Clang / MSVC +* Конфигурация: Release + +Окружение: +* `PPC_NUM_THREADS`: задает число потоков (1–6). +* `PPC_NUM_PROC`: задает число MPI-процессов для ALL. + +Генерация данных: +* Используются ленточные матрицы размера 40,000x40,000 с шириной полосы 30. + +## 7. Results and Discussion +### 7.1 Correctness +Корректность проверялась функциональными тестами. Вычисленное произведение сравнивается с эталонным результатом (полученным последовательно) для единичных, прямоугольных и разреженных ленточных матриц. Допустимая погрешность: $10^{-10}$. + +### 7.2 Performance +Mode | Count | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A +omp | 6 | 0.174125 | 4.89 | 81.50% +tbb | 6 | 0.165214 | 5.16 | 86.00% +stl | 6 | 0.181245 | 4.70 | 78.33% +all | 2 x 3 | 0.192451 | 4.43 | 73.83% + +## 8. Conclusions +Все параллельные реализации продемонстрировали существенное ускорение. Наилучший результат показала технология TBB за счет эффективной балансировки нагрузки при работе с разреженными данными. Основное ограничение масштабируемости связано с накладными расходами на выделение памяти для локальных аккумуляторов строк и финальную последовательную сборку CRS-структуры в общей памяти. + +## 9. References +* OpenMP Architecture Review Board. OpenMP Application Programming Interface. +* oneAPI Threading Building Blocks Documentation. +* Microsoft MPI Documentation. +* ISO C++ Standard Library Documentation: std::thread, std::async. + +## Appendix (Optional) +Ниже приведены основные фрагменты RunImpl() для всех реализаций. + +### SEQ RunImpl +```cpp +for (int row_index = 0; row_index < matrix_a.rows; ++row_index) { + std::unordered_map accumulator; + for (int index_a = matrix_a.row_ptr[row_index]; index_a < matrix_a.row_ptr[row_index + 1]; ++index_a) { + int col_a = matrix_a.col_index[index_a]; + double value_a = matrix_a.values[index_a]; + for (int index_b = matrix_b.row_ptr[col_a]; index_b < matrix_b.row_ptr[col_a + 1]; ++index_b) { + accumulator[matrix_b.col_index[index_b]] += value_a * matrix_b.values[index_b]; + } + } + // Sorting and saving... +} + +### OMP RunImpl +```cpp +#pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a) +for (int i = 0; i < rows_a; ++i) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[i]; j < matrix_a.row_ptr[i + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += matrix_a.values[j] * matrix_b.values[k]; + } + } +} + +### TBB RunImpl +```cpp +tbb::parallel_for(0, rows_a, [&](int i) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[i]; j < matrix_a.row_ptr[i + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += matrix_a.values[j] * matrix_b.values[k]; + } + } +}); + +### STL RunImpl +```cpp +for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + futures.push_back(std::async(std::launch::async, [=, &matrix_a, &matrix_b, &local_cols, &local_vals] { + for (int i = start_row; i < end_row; ++i) { + MultiplyRow(i, matrix_a, matrix_b, local_cols[i], local_vals[i]); + } + })); +} + +### ALL RunImpl +```cpp +MPI_Comm_rank(MPI_COMM_WORLD, &rank); +MPI_Comm_size(MPI_COMM_WORLD, &size); +// Process local stripes of matrix A +for (int t = 0; t < thread_count; ++t) { + threads.emplace_back(compute_rows, s, e); // Inside: tbb::parallel_for +} +for (auto &th : threads) th.join(); +// Collect results using MPI_Allgatherv +MPI_Allgatherv(my_flat_cols.data(), ..., matrix_c.col_index.data(), ...); \ No newline at end of file diff --git a/tasks/ashihmin_d_mult_matr_crs/seq/report.md b/tasks/ashihmin_d_mult_matr_crs/seq/report.md new file mode 100644 index 0000000000..a80528be81 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/seq/report.md @@ -0,0 +1,102 @@ +# Умножение разреженных матриц в формате CRS - SEQ + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: SEQ +* Variant: 4 + +## 1. Introduction +Последовательная версия используется как базовая линия производительности. Она реализует классический алгоритм умножения разреженных матриц в формате CRS, который служит эталоном для проверки корректности результатов параллельных реализаций и расчета ускорения. + +## 2. Problem Statement +Вход: `InType = std::pair`, где каждая матрица представлена структурой с полями `rows`, `cols`, `row_ptr`, `col_index`, `values`. +Выход: `OutType = CRSMatrix`, результат умножения матриц A и B. +Ограничения: `matrix_a.cols == matrix_b.rows`, элементы типа `double`. + +## 3. Baseline Algorithm (Sequential) +SEQ-версия использует алгоритм «строка на матрицу». Для каждой строки $i$ матрицы A создается временный аккумулятор (`std::unordered_map`). Алгоритм проходит по всем ненулевым элементам строки $i$ матрицы A. Для каждого элемента $A_{ik}$ находится соответствующая строка $k$ матрицы B, и её значения, умноженные на $A_{ik}$, добавляются в аккумулятор. +После завершения обработки строки, данные из аккумулятора сортируются по индексам столбцов, фильтруются от нулевых значений и записываются в итоговую структуру CRS. + +## 4. Parallelization Scheme +Параллелизм отсутствует. Все операции выполняются последовательно в одном потоке. `workers = 1`. + +## 5. Implementation Details +* Файлы: `seq/include/ops_seq.hpp`, `seq/src/ops_seq.cpp`. +* Класс: `AshihminDMultMatrCrsSEQ`. +* `ValidationImpl()`: Проверяет совместимость матриц по размерности. +* `RunImpl()`: Реализует основной расчет с использованием `std::unordered_map` для накопления строк и `std::ranges::sort` для упорядочивания столбцов. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: 12th Gen Intel(R) Core(TM) i5-12450H (8 ядер / 12 потоков) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) + +Генерация данных: +* Для performance-теста используется ленточная матрица размера 40,000x40,000. +* Ширина ленты (bandwidth): 30. + +## 7. Results and Discussion +### 7.1 Correctness +Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с эталонными результатами для единичных и прямоугольных матриц с точностью $10^{-10}$. + +### 7.2 Performance +Mode | Count | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A + +## 8. Conclusions +Последовательная версия обеспечивает корректное перемножение разреженных матриц. Она является baseline для оценки эффективности многопоточных версий. + +## 9. References +1. Microsoft MPI Documentation. +2. ISO C++ Standard Library Documentation: std::thread. +3. Sparse Matrix-Matrix Multiplication Algorithms. + +## Appendix (Optional) +Основной фрагмент RunImpl(): +```cpp +bool AshihminDMultMatrCrsSEQ::RunImpl() { + const auto &matrix_a = GetInput().first; + const auto &matrix_b = GetInput().second; + auto &matrix_c = GetOutput(); + + matrix_c.values.clear(); + matrix_c.col_index.clear(); + + for (int row_index = 0; row_index < matrix_a.rows; ++row_index) { + std::unordered_map accumulator; + + auto row_begin = static_cast(matrix_a.row_ptr[row_index]); + auto row_end = static_cast(matrix_a.row_ptr[row_index + 1]); + + for (std::size_t index_a = row_begin; index_a < row_end; ++index_a) { + int col_a = matrix_a.col_index[index_a]; + double value_a = matrix_a.values[index_a]; + + auto col_begin = static_cast(matrix_b.row_ptr[col_a]); + auto col_end = static_cast(matrix_b.row_ptr[col_a + 1]); + + for (std::size_t index_b = col_begin; index_b < col_end; ++index_b) { + int col_b = matrix_b.col_index[index_b]; + double value_b = matrix_b.values[index_b]; + + accumulator[col_b] += value_a * value_b; + } + } + + std::vector> sorted_values(accumulator.begin(), accumulator.end()); + + std::ranges::sort(sorted_values, {}, &std::pair::first); + + for (const auto &entry : sorted_values) { + if (entry.second != 0.0) { + matrix_c.values.push_back(entry.second); + matrix_c.col_index.push_back(entry.first); + } + } + + matrix_c.row_ptr[row_index + 1] = static_cast(matrix_c.values.size()); + } + + return true; +} \ No newline at end of file diff --git a/tasks/ashihmin_d_mult_matr_crs/stl/report.md b/tasks/ashihmin_d_mult_matr_crs/stl/report.md new file mode 100644 index 0000000000..4171046b18 --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/stl/report.md @@ -0,0 +1,80 @@ +# Умножение разреженных матриц в формате CRS - STL + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: STL +* Variant: 4 + +## 1. Introduction +STL-версия использует стандартные механизмы многопоточности C++, такие как `std::async` и `std::future`. Эта реализация демонстрирует, как задачу умножения разреженных матриц можно эффективно распараллелить, используя исключительно средства стандартной библиотеки (ISO C++), что обеспечивает максимальную переносимость кода без зависимости от OpenMP или TBB. + +## 2. Problem Statement +Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). +Вход: InType = std::pair. +Выход: OutType = CRSMatrix, результат C = A * B. +Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. + +## 3. Baseline Algorithm (Sequential) +Baseline описан в seq/report.md. Последовательный алгоритм выполняет обход строк матрицы A, вычисляет ненулевые значения результирующей строки через вспомогательный аккумулятор и сохраняет их в структуру CRS. + +## 4. Parallelization Scheme +Для распределения нагрузки используется декомпозиция по строкам матрицы A. Весь диапазон строк делится на блоки (chunks), количество которых соответствует числу аппаратных потоков: + +* **Определение потоков:** thread_count берется из `std::thread::hardware_concurrency()`. +* **Разбиение на блоки:** Каждому потоку назначается диапазон строк `[start_row, end_row)`. +* **Запуск задач:** Для каждого блока вызывается `std::async` с политикой `std::launch::async`, что гарантирует выполнение в отдельном потоке. +* **Вычисления:** Внутри каждой задачи выполняется цикл по назначенным строкам. Для каждой строки вызывается метод `MultiplyRow`, использующий локальный `std::map` для накопления значений. +* **Синхронизация:** Главный поток сохраняет объекты `std::future` в вектор и дожидается завершения всех задач через вызов `.get()`. +* **Безопасность:** Гонок данных нет, так как каждый поток пишет результаты только в свои индексы векторов `local_cols` и `local_vals`. + +## 5. Implementation Details +* Файлы: stl/include/ops_stl.hpp, stl/src/ops_stl.cpp. +* Класс: AshihminDMultMatrCrsSTL. +* **MultiplyRow:** Логика вычисления одной строки вынесена в статический метод для снижения когнитивной сложности и удовлетворения требований Clang-Tidy. +* **Память:** Дополнительная память используется для хранения промежуточных локальных векторов каждой строки перед финальной сборкой. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) + +Инструменты: +* Сборка: CMake +* Компилятор: GCC / Clang / MSVC (стандарт C++20) + +## 7. Results and Discussion +### 7.1 Correctness +Корректность проверялась функциональными тестами из tests/functional/main.cpp. Вычисленные значения сравниваются с результатами последовательной версии для различных типов матриц (единичные, прямоугольные, случайные) с точностью 1e-10. + +### 7.2 Performance +Mode | Count | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A +stl | 2 | 0.463267 | 1.84 | 92.00% +stl | 4 | 0.247794 | 3.44 | 86.00% +stl | 6 | 0.181245 | 4.70 | 78.33% + +## 8. Conclusions +Реализация на базе STL (`std::async`) показала производительность, сопоставимую с OpenMP. Использование chunks (блоков строк) вместо создания потока на каждую строку позволило минимизировать накладные расходы на управление задачами. + +## 9. References +1. ISO C++ Standard Library Documentation: std::thread, std::async. +2. Anthony Williams. C++ Concurrency in Action. +3. OpenMP Architecture Review Board. OpenMP API. +4. Microsoft MPI Documentation. + +## Appendix (Optional) +Фрагмент RunImpl() с использованием std::async: +```cpp +for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + int start_row = thread_idx * chunk_size; + int end_row = std::min(start_row + chunk_size, rows_a); + if (start_row >= end_row) break; + + futures.push_back(std::async(std::launch::async, [=, &matrix_a, &matrix_b, &local_cols, &local_vals] { + for (int i = start_row; i < end_row; ++i) { + MultiplyRow(i, matrix_a, matrix_b, local_cols[i], local_vals[i]); + } + })); +} +for (auto &fut : futures) fut.get(); \ No newline at end of file diff --git a/tasks/ashihmin_d_mult_matr_crs/tbb/report.md b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md new file mode 100644 index 0000000000..0534b2aacf --- /dev/null +++ b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md @@ -0,0 +1,80 @@ +# Умножение разреженных матриц в формате CRS - TBB + +* Student: Ашихмин Д., group 3823Б1ФИ2 +* Technology: TBB +* Variant: 4 + +## 1. Introduction +TBB-версия использует задачно-ориентированный подход Intel oneAPI Threading Building Blocks. Вместо явного создания потоков, общий диапазон строк матрицы передаётся планировщику TBB, который разбивает его на подзадачи и распределяет работу между потоками, используя алгоритм кражи задач (work-stealing). + +## 2. Problem Statement +Требуется реализовать умножение двух разреженных матриц A и B, представленных в строковом формате CRS (Compressed Row Storage). +Вход: InType = std::pair. +Выход: OutType = CRSMatrix, произведение C = A * B. +Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. + +## 3. Baseline Algorithm (Sequential) +Baseline описан в seq/report.md. Один поток последовательно обходит строки матрицы A, накапливает ненулевые значения во временном словаре и формирует результирующие векторы values и col_index для каждой строки. + +## 4. Parallelization Scheme +Используется функция tbb::parallel_for для распределения вычислений строк результирующей матрицы. + +* **Диапазон:** tbb::parallel_for принимает диапазон индексов от 0 до количества строк матрицы A. +* **Планировщик:** Используется стандартный механизм TBB. Это эффективно для разреженных матриц, так как плотность строк может сильно различаться, а динамическая балансировка нагрузки (work-stealing) позволяет избежать простоя ядер. +* **Локальность данных:** Каждый рабочий поток внутри лямбда-выражения создает свой std::map row_accumulator. Это обеспечивает потокобезопасность без использования блокировок. +* **Автоматическая сортировка:** Использование std::map гарантирует, что индексы столбцов внутри каждой строки будут отсортированы, что необходимо для корректности формата CRS. +* **Контроль конкуренции:** Количество используемых потоков ограничивается через настройки тестового фреймворка PPC (переменная PPC_NUM_THREADS). + +## 5. Implementation Details +* Файлы: tbb/include/ops_tbb.hpp, tbb/src/ops_tbb.cpp. +* Класс: AshihminDMultMatrCrsTBB. +* Результаты каждой итерации записываются в заранее подготовленные локальные векторы local_cols[i] и local_vals[i]. +* Гонок данных нет, так как каждый таск работает с уникальным индексом строки i и пишет в свою область памяти. +* Сборка финальной структуры (объединение локальных векторов в один плоский массив) выполняется последовательно после завершения параллельного цикла. + +## 6. Experimental Setup +Аппаратное обеспечение: +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) + +Инструменты: +* Сборка: CMake +* Компилятор: GCC / Clang / MSVC +* Библиотека: Intel oneAPI TBB + +## 7. Results and Discussion +7.1 Correctness +Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с результатами последовательной версии для различных конфигураций: единичные, диагональные и прямоугольные матрицы. + +7.2 Performance +Mode | Count | Time, s | Speedup | Efficiency +--- | --- | --- | --- | --- +seq | 1 | 0.852412 | 1.00 | N/A +tbb | 2 | 0.441664 | 1.93 | 96.5% +tbb | 4 | 0.226705 | 3.76 | 94.0% +tbb | 6 | 0.165214 | 5.16 | 86.0% + +## 8. Conclusions +Реализация на TBB показала высокую эффективность и отличную масштабируемость. За счет механизма динамического распределения задач (work-stealing), версия TBB эффективно справляется с неравномерной плотностью строк в разреженных матрицах. + +## 9. References +1. oneAPI Threading Building Blocks Documentation. +2. OpenMP Architecture Review Board. OpenMP API. +3. Microsoft MPI Documentation. +4. ISO C++ Standard Library Documentation: std::thread. + +## Appendix (Optional) +Основной фрагмент RunImpl(): +```cpp +tbb::parallel_for(0, rows_a, [&](int i) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[i]; j < matrix_a.row_ptr[i + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; + } + } + // Filter and save... +}); \ No newline at end of file From 3f58c6a55f8e3e2b0d82a7a53e229512e3446cbd Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 16:08:15 +0300 Subject: [PATCH 4/7] report + all fix --- tasks/ashihmin_d_mult_matr_crs/report.md | 63 +++++++++++++++--------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/tasks/ashihmin_d_mult_matr_crs/report.md b/tasks/ashihmin_d_mult_matr_crs/report.md index 424f30f89f..9317728c0d 100644 --- a/tasks/ashihmin_d_mult_matr_crs/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/report.md @@ -5,9 +5,14 @@ * Variant: 4 ## 1. Introduction -Работа посвящена реализации высокопроизводительного алгоритма умножения разреженных матриц, хранящихся в строковом формате CRS. Этот формат является стандартом для работы с большими разреженными системами, так как позволяет хранить только ненулевые элементы. В работе реализованы пять вариантов задачи: последовательный baseline и параллельные версии с использованием OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). +Работа посвящена реализации высокопроизводительного алгоритма умножения разреженных матриц, +хранящихся в строковом формате CRS. Этот формат является стандартом для работы с большими +разреженными системами, так как позволяет хранить только ненулевые элементы. В работе реализованы +пять вариантов задачи: последовательный baseline и параллельные версии с использованием +OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). -Ожидаемый результат работы — корректное произведение матриц и сравнение эффективности различных технологий параллелизма при масштабировании на 6-ядерном процессоре. +Ожидаемый результат работы — корректное произведение матриц и сравнение эффективности различных +технологий параллелизма при масштабировании на 6-ядерном процессоре. ## 2. Problem Statement Входные данные задаются типом `InType = std::pair`: @@ -16,23 +21,32 @@ Выходные данные: `OutType = CRSMatrix`, результат операции $C = A \times B$. -Формат CRS включает три основных вектора: `values` (значения), `col_index` (номера столбцов) и `row_ptr` (индексы начала строк). Ограничения: число столбцов матрицы A должно быть равно числу строк матрицы B (`matrix_a.cols == matrix_b.rows`). Элементы имеют тип `double`. +Формат CRS включает три основных вектора: `values` (значения), `col_index` (номера столбцов) +и `row_ptr` (индексы начала строк). Ограничения: число столбцов матрицы A должно быть +равно числу строк матрицы B (`matrix_a.cols == matrix_b.rows`). Элементы имеют тип `double`. ## 3. Baseline Algorithm (Sequential) -Последовательная версия служит baseline для всех параллельных реализаций. Алгоритм реализует схему «строка на матрицу». Для каждой строки $i$ матрицы A: +Последовательная версия служит baseline для всех параллельных реализаций. Алгоритм реализует +схему «строка на матрицу». Для каждой строки $i$ матрицы A: 1. Используется временный аккумулятор `std::unordered_map` для накопления значений. 2. Просматриваются ненулевые элементы $A_{ik}$. 3. Для каждого $A_{ik}$ выбирается соответствующая строка $k$ матрицы B. 4. Вычисляются произведения $A_{ik} \times B_{kj}$ и суммируются в аккумуляторе по индексу $j$. -В конце обработки строки элементы аккумулятора сортируются по индексу столбца и переносятся в итоговую структуру. Время выполнения SEQ считается базовым (speedup = 1.0). +В конце обработки строки элементы аккумулятора сортируются по индексу столбца и переносятся +в итоговую структуру. Время выполнения SEQ считается базовым (speedup = 1.0). ## 4. Parallelization Scheme * **SEQ**: один поток, параллелизм не используется. -* **OMP**: параллельный цикл по строкам матрицы A с использованием `#pragma omp parallel for`. Каждый поток имеет локальный аккумулятор `std::map`. -* **TBB**: использование `tbb::parallel_for` для автоматического распределения строк между потоками планировщиком TBB (work-stealing). -* **STL**: ручное разбиение диапазона строк на блоки (chunks) и запуск их через `std::async` с политикой `launch::async`. -* **ALL**: гибридная схема. MPI делит строки между процессами. Внутри процесса используются STL-потоки для разделения задач, TBB для мелкозернистого параллелизма и OpenMP для обработки метаданных. Глобальная сборка разреженной матрицы выполняется через `MPI_Allgatherv`. +* **OMP**: параллельный цикл по строкам матрицы A с использованием `#pragma omp parallel for`. + Каждый поток имеет локальный аккумулятор `std::map`. +* **TBB**: использование `tbb::parallel_for` для автоматического распределения строк между + потоками планировщиком TBB (work-stealing). +* **STL**: ручное разбиение диапазона строк на блоки (chunks) и запуск их через `std::async` + с политикой `launch::async`. +* **ALL**: гибридная схема. MPI делит строки между процессами. Внутри процесса используются + STL-потоки для разделения задач, TBB для мелкозернистого параллелизма и OpenMP для обработки + метаданных. Глобальная сборка разреженной матрицы выполняется через `MPI_Allgatherv`. ## 5. Implementation Details Код задачи расположен в папке `tasks/ashihmin_d_mult_matr_crs`: @@ -44,7 +58,8 @@ * `all/src/ops_all.cpp` — гибридная версия ALL; * `tests/` — тесты производительности и корректности. -Во всех параллельных версиях вместо `unordered_map` применен `std::map`, что гарантирует автоматическую сортировку столбцов согласно спецификации CRS. +Во всех параллельных версиях вместо `unordered_map` применен `std::map`, что гарантирует +автоматическую сортировку столбцов согласно спецификации CRS. ## 6. Experimental Setup Аппаратное обеспечение: @@ -66,7 +81,9 @@ ## 7. Results and Discussion ### 7.1 Correctness -Корректность проверялась функциональными тестами. Вычисленное произведение сравнивается с эталонным результатом (полученным последовательно) для единичных, прямоугольных и разреженных ленточных матриц. Допустимая погрешность: $10^{-10}$. +Корректность проверялась функциональными тестами. Вычисленное произведение сравнивается с +эталонным результатом (полученным последовательно) для единичных, прямоугольных и разреженных +ленточных матриц. Допустимая погрешность: $10^{-10}$. ### 7.2 Performance Mode | Count | Time, s | Speedup | Efficiency @@ -78,7 +95,10 @@ stl | 6 | 0.181245 | 4.70 | 78.33% all | 2 x 3 | 0.192451 | 4.43 | 73.83% ## 8. Conclusions -Все параллельные реализации продемонстрировали существенное ускорение. Наилучший результат показала технология TBB за счет эффективной балансировки нагрузки при работе с разреженными данными. Основное ограничение масштабируемости связано с накладными расходами на выделение памяти для локальных аккумуляторов строк и финальную последовательную сборку CRS-структуры в общей памяти. +Все параллельные реализации продемонстрировали существенное ускорение. Наилучший результат +показала технология TBB за счет балансировки нагрузки. Основное ограничение масштабируемости +связано с накладными расходами на выделение памяти для локальных аккумуляторов строк и +финальную последовательную сборку CRS-структуры в общей памяти. ## 9. References * OpenMP Architecture Review Board. OpenMP Application Programming Interface. @@ -93,16 +113,15 @@ all | 2 x 3 | 0.192451 | 4.43 | 73.83% ```cpp for (int row_index = 0; row_index < matrix_a.rows; ++row_index) { std::unordered_map accumulator; - for (int index_a = matrix_a.row_ptr[row_index]; index_a < matrix_a.row_ptr[row_index + 1]; ++index_a) { - int col_a = matrix_a.col_index[index_a]; - double value_a = matrix_a.values[index_a]; - for (int index_b = matrix_b.row_ptr[col_a]; index_b < matrix_b.row_ptr[col_a + 1]; ++index_b) { - accumulator[matrix_b.col_index[index_b]] += value_a * matrix_b.values[index_b]; + for (int j = matrix_a.row_ptr[row_index]; j < matrix_a.row_ptr[row_index + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; } } - // Sorting and saving... } - +``` ### OMP RunImpl ```cpp #pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a) @@ -115,7 +134,7 @@ for (int i = 0; i < rows_a; ++i) { } } } - +``` ### TBB RunImpl ```cpp tbb::parallel_for(0, rows_a, [&](int i) { @@ -127,7 +146,7 @@ tbb::parallel_for(0, rows_a, [&](int i) { } } }); - +``` ### STL RunImpl ```cpp for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { @@ -137,7 +156,7 @@ for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { } })); } - +``` ### ALL RunImpl ```cpp MPI_Comm_rank(MPI_COMM_WORLD, &rank); From 105184bf971e16e9989dc5da5cc9de29ff5dee79 Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 16:15:52 +0300 Subject: [PATCH 5/7] report + all fix1 --- tasks/ashihmin_d_mult_matr_crs/all/report.md | 48 ++++++++++++++------ tasks/ashihmin_d_mult_matr_crs/omp/report.md | 29 ++++++++---- tasks/ashihmin_d_mult_matr_crs/seq/report.md | 23 +++++++--- tasks/ashihmin_d_mult_matr_crs/stl/report.md | 42 ++++++++++++----- tasks/ashihmin_d_mult_matr_crs/tbb/report.md | 34 ++++++++++---- 5 files changed, 126 insertions(+), 50 deletions(-) diff --git a/tasks/ashihmin_d_mult_matr_crs/all/report.md b/tasks/ashihmin_d_mult_matr_crs/all/report.md index e145e5c4c8..b5e584e986 100644 --- a/tasks/ashihmin_d_mult_matr_crs/all/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/all/report.md @@ -5,34 +5,50 @@ * Variant: 4 ## 1. Introduction -ALL-версия представляет собой гибридную реализацию, объединяющую несколько технологий параллелизма. Цель этой реализации — распределить вычисления между узлами кластера (процессами MPI), а внутри каждого процесса максимально эффективно загрузить доступные ядра процессора, используя комбинацию STL Threads, TBB и OpenMP. +ALL-версия представляет собой гибридную реализацию, объединяющую несколько технологий +параллелизма. Цель этой реализации — распределить вычисления между узлами кластера (процессами MPI), а внутри каждого процесса максимально +эффективно загрузить доступные ядра процессора, используя комбинацию STL Threads, TBB и OpenMP. ## 2. Problem Statement -Задача: реализовать умножение двух разреженных матриц A и B в формате CRS (Compressed Row Storage). +Задача: реализовать умножение двух разреженных матриц A и B в формате CRS +(Compressed Row Storage). Вход: InType = std::pair. Выход: OutType = CRSMatrix, результат C = A * B. -Особенности: Алгоритм должен поддерживать как распределенную память (MPI), так и общую память (Threads). +Особенности: Алгоритм должен поддерживать как распределенную память (MPI), так и +общую память (Threads). ## 3. Baseline Algorithm (Sequential) -Baseline описан в seq/report.md. Он выполняет последовательный проход по всем строкам матрицы A. Результат используется для верификации корректности гибридной версии и расчета итогового ускорения. +Baseline описан в seq/report.md. Он выполняет последовательный проход по всем строкам +матрицы A. Результат используется для верификации корректности гибридной версии и расчета +итогового ускорения. ## 4. Parallelization Scheme ALL-реализация использует многоуровневую гибридную схему: -1. **MPI (Уровень процессов):** Общий диапазон строк матрицы A делится между MPI-рангами. Каждый процесс отвечает за вычисление своей "полосы" результирующей матрицы. -2. **STL Threads (Уровень потоков):** Внутри каждого MPI-ранга локальный диапазон строк делится на блоки (chunks) и распределяется между потоками `std::thread`. -3. **TBB (Микро-параллелизм):** Внутри каждого STL-потока вызывается функция обработки, где используется `tbb::parallel_for` для мелкозернистого распараллеливания вычислений внутри блока строк. -4. **OpenMP (Вспомогательный уровень):** Используется внутри процесса для параллельного подсчета количества ненулевых элементов (NNZ) в строках через директиву `#pragma omp parallel for`. -5. **MPI-синхронизация:** После вычисления локальных частей выполняется сложная процедура сборки. Сначала через `MPI_Allgatherv` собираются размеры строк, затем восстанавливается глобальный `row_ptr`, и в конце через еще один `MPI_Allgatherv` собираются векторы `values` и `col_index`. +1. **MPI (Уровень процессов):** Общий диапазон строк матрицы A делится между MPI-рангами. +Каждый процесс отвечает за вычисление своей "полосы" результирующей матрицы. +2. **STL Threads (Уровень потоков):** Внутри каждого MPI-ранга локальный диапазон строк +делится на блоки (chunks) и распределяется между потоками `std::thread`. +3. **TBB (Микро-параллелизм):** Внутри каждого STL-потока вызывается функция обработки, +где используется `tbb::parallel_for` для мелкозернистого распараллеливания вычислений внутри блока строк. +4. **OpenMP (Вспомогательный уровень):** Используется внутри процесса для +параллельного подсчета количества ненулевых элементов (NNZ) в строках через директиву `#pragma omp parallel for`. +5. **MPI-синхронизация:** После вычисления локальных частей выполняется сложная +процедура сборки. Сначала через `MPI_Allgatherv` собираются размеры строк, затем +восстанавливается глобальный `row_ptr`, и в конце через еще один `MPI_Allgatherv` +собираются векторы `values` и `col_index`. Конфигурация задается как workers = ranks × threads. ## 5. Implementation Details * Файлы: all/include/ops_all.hpp, all/src/ops_all.cpp. * Класс: AshihminDMultMatrCrsALL. -* **MPI_Allgatherv:** Применяется для обмена данными между процессами, так как результирующие части матрицы имеют разный размер (из-за разреженности). -* **Балансировка:** Использование TBB внутри STL-потоков позволяет планировщику TBB динамически балансировать нагрузку, если строки матрицы имеют разную плотность. -* **Память:** Каждый процесс хранит полную копию матрицы B и часть матрицы A, что типично для алгоритмов SpGEMM на небольших кластерах. +* **MPI_Allgatherv:** Применяется для обмена данными между процессами, так как результирующие +части матрицы имеют разный размер (из-за разреженности). +* **Балансировка:** Использование TBB внутри STL-потоков позволяет планировщику TBB динамически +балансировать нагрузку, если строки матрицы имеют разную плотность. +* **Память:** Каждый процесс хранит полную копию матрицы B и часть матрицы A, что типично для +алгоритмов SpGEMM на небольших кластерах. ## 6. Experimental Setup Аппаратное обеспечение: @@ -47,7 +63,8 @@ ALL-реализация использует многоуровневую ги ## 7. Results and Discussion ### 7.1 Correctness -Корректность проверялась функциональными тестами. После сборки `MPI_Allgatherv` каждый процесс обладает полной и корректной копией матрицы C, идентичной результату последовательной версии. +Корректность проверялась функциональными тестами. После сборки `MPI_Allgatherv` каждый процесс обладает +полной и корректной копией матрицы C, идентичной результату последовательной версии. ### 7.2 Performance Mode | Count (R x T) | Time, s | Speedup | Efficiency @@ -58,7 +75,10 @@ all | 2 x 3 | 0.192451 | 4.43 | 73.83% all | 3 x 2 | 0.198523 | 4.29 | 71.50% ## 8. Conclusions -Гибридная реализация демонстрирует возможность масштабирования алгоритма умножения CRS матриц. Основная сложность заключается в накладных расходах на коммуникации MPI при сборке разреженной структуры, так как объемы передаваемых данных заранее неизвестны. Комбинация TBB и MPI позволяет эффективно использовать как ресурсы одного узла, так и вычислительную мощность всей сети. +Гибридная реализация демонстрирует возможность масштабирования алгоритма умножения CRS матриц. +Основная сложность заключается в накладных расходах на коммуникации MPI при сборке разреженной +структуры, так как объемы передаваемых данных заранее неизвестны. Комбинация TBB и MPI позволяет +эффективно использовать как ресурсы одного узла, так и вычислительную мощность всей сети. ## 9. References 1. OpenMP Architecture Review Board. OpenMP API. diff --git a/tasks/ashihmin_d_mult_matr_crs/omp/report.md b/tasks/ashihmin_d_mult_matr_crs/omp/report.md index f395b2ee2a..ebe9dbaabc 100644 --- a/tasks/ashihmin_d_mult_matr_crs/omp/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/omp/report.md @@ -5,16 +5,21 @@ * Variant: 4 ## 1. Introduction -OpenMP-версия реализует параллельное вычисление строк результирующей матрицы в общей памяти. Задача умножения матриц в формате CRS (Compressed Row Storage) по схеме «строка на матрицу» хорошо поддается распараллеливанию, так как вычисление каждой строки итоговой матрицы не зависит от результатов вычисления других строк. +OpenMP-версия реализует параллельное вычисление строк результирующей матрицы в +общей памяти. Задача умножения матриц в формате CRS (Compressed Row Storage) по +схеме «строка на матрицу» хорошо поддается распараллеливанию, так как вычисление +каждой строки итоговой матрицы не зависит от результатов вычисления других строк. ## 2. Problem Statement -Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). +Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц +A и B, представленных в строковом формате (CRS). Вход: `InType = std::pair`. Выход: `OutType = CRSMatrix`, представляющая собой произведение $C = A \times B$. Ограничения: количество столбцов матрицы A равно количеству строк матрицы B. ## 3. Baseline Algorithm (Sequential) -Baseline описан в `seq/report.md`. Он выполняет последовательный обход строк матрицы A, накапливает ненулевые элементы в `std::unordered_map` и затем формирует итоговые векторы CRS. +Baseline описан в `seq/report.md`. Он выполняет последовательный обход строк +матрицы A, накапливает ненулевые элементы в `std::unordered_map` и затем формирует итоговые векторы CRS. ## 4. Parallelization Scheme В OMP-версии параллелится внешний цикл по строкам матрицы A: @@ -24,13 +29,16 @@ Baseline описан в `seq/report.md`. Он выполняет последо * **shared**: `matrix_a`, `matrix_b` — исходные данные только для чтения. `local_cols`, `local_vals` — контейнеры для записи результатов каждой строки. * **private**: внутри каждой итерации создается `std::map row_accumulator`. Это локальный аккумулятор потока для текущей строки $i$. * **Изоляция данных**: использование локального аккумулятора и запись в заранее выделенные индексы векторов `local_cols[i]` и `local_vals[i]` исключает состояние гонки (data race). -* **Сортировка**: использование `std::map` вместо `unordered_map` внутри потока гарантирует, что индексы столбцов в итоговой строке будут автоматически отсортированы, что является требованием формата CRS. -* **Синхронизация**: явные барьеры не требуются, так как потоки пишут в непересекающиеся области памяти (разные элементы внешних векторов). Финальная сборка CRS структуры выполняется последовательно после завершения параллельной секции. +* **Сортировка**: использование `std::map` вместо `unordered_map` внутри потока гарантирует, +что индексы столбцов в итоговой строке будут автоматически отсортированы, что является требованием формата CRS. +* **Синхронизация**: явные барьеры не требуются, так как потоки пишут в непересекающиеся +области памяти (разные элементы внешних векторов). Финальная сборка CRS структуры выполняется последовательно после завершения параллельной секции. ## 5. Implementation Details * Файлы: `omp/include/ops_omp.hpp`, `omp/src/ops_omp.cpp`. * Класс: `AshihminDMultMatrCrsOMP`. -* Память: для каждого потока динамически выделяется память под `std::map` и временные векторы строки. Это обеспечивает независимость, но накладывает накладные расходы на аллокатор. +* Память: для каждого потока динамически выделяется память под `std::map` и временные +векторы строки. Это обеспечивает независимость, но накладывает накладные расходы на аллокатор. * Фильтрация: элементы, результат которых по модулю меньше $10^{-15}$, отсеиваются как нулевые. ## 6. Experimental Setup @@ -50,7 +58,9 @@ Baseline описан в `seq/report.md`. Он выполняет последо ## 7. Results and Discussion ### 7.1 Correctness -Корректность проверялась функциональными тестами из `tests/functional/main.cpp`. Для проверки используются тесты на единичных матрицах, прямоугольных матрицах разного размера и полностью нулевых матрицах. Сравнение выполняется с точностью $10^{-10}$. +Корректность проверялась функциональными тестами из `tests/functional/main.cpp`. Для +проверки используются тесты на единичных матрицах, прямоугольных матрицах разного размера +и полностью нулевых матрицах. Сравнение выполняется с точностью $10^{-10}$. ### 7.2 Performance Используемые обозначения: @@ -67,7 +77,10 @@ omp | 4 | 0.236781 | 3.60 | 90.0% omp | 6 | 0.174125 | 4.89 | 81.5% ## 8. Conclusions -Реализация эффективно использует возможности многоядерных процессоров через OpenMP. Достигнуто значительное ускорение (почти 5-кратное на 6 ядрах). Основным ограничивающим фактором масштабируемости является интенсивная работа с динамической памятью (создание `std::map` в каждой строке) и финальная последовательная сборка CRS-структуры. +Реализация эффективно использует возможности многоядерных процессоров через OpenMP. +Достигнуто значительное ускорение (почти 5-кратное на 6 ядрах). Основным ограничивающим +фактором масштабируемости является интенсивная работа с динамической памятью +(создание `std::map` в каждой строке) и финальная последовательная сборка CRS-структуры. ## 9. References * OpenMP Architecture Review Board. OpenMP Application Programming Interface. diff --git a/tasks/ashihmin_d_mult_matr_crs/seq/report.md b/tasks/ashihmin_d_mult_matr_crs/seq/report.md index a80528be81..dff745fb3b 100644 --- a/tasks/ashihmin_d_mult_matr_crs/seq/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/seq/report.md @@ -5,16 +5,25 @@ * Variant: 4 ## 1. Introduction -Последовательная версия используется как базовая линия производительности. Она реализует классический алгоритм умножения разреженных матриц в формате CRS, который служит эталоном для проверки корректности результатов параллельных реализаций и расчета ускорения. +Последовательная версия используется как базовая линия производительности. +Она реализует классический алгоритм умножения разреженных матриц в формате +CRS, который служит эталоном для проверки корректности результатов +параллельных реализаций и расчета ускорения. ## 2. Problem Statement -Вход: `InType = std::pair`, где каждая матрица представлена структурой с полями `rows`, `cols`, `row_ptr`, `col_index`, `values`. +Вход: `InType = std::pair`, где каждая матрица представлена +структурой с полями `rows`, `cols`, `row_ptr`, `col_index`, `values`. Выход: `OutType = CRSMatrix`, результат умножения матриц A и B. Ограничения: `matrix_a.cols == matrix_b.rows`, элементы типа `double`. ## 3. Baseline Algorithm (Sequential) -SEQ-версия использует алгоритм «строка на матрицу». Для каждой строки $i$ матрицы A создается временный аккумулятор (`std::unordered_map`). Алгоритм проходит по всем ненулевым элементам строки $i$ матрицы A. Для каждого элемента $A_{ik}$ находится соответствующая строка $k$ матрицы B, и её значения, умноженные на $A_{ik}$, добавляются в аккумулятор. -После завершения обработки строки, данные из аккумулятора сортируются по индексам столбцов, фильтруются от нулевых значений и записываются в итоговую структуру CRS. +SEQ-версия использует алгоритм «строка на матрицу». Для каждой строки $i$ матрицы A +создается временный аккумулятор (`std::unordered_map`). Алгоритм проходит по всем +ненулевым элементам строки $i$ матрицы A. Для каждого элемента $A_{ik}$ находится +соответствующая строка $k$ матрицы B, и её значения, умноженные на $A_{ik}$, добавляются +в аккумулятор. +После завершения обработки строки, данные из аккумулятора сортируются по индексам столбцов, +фильтруются от нулевых значений и записываются в итоговую структуру CRS. ## 4. Parallelization Scheme Параллелизм отсутствует. Все операции выполняются последовательно в одном потоке. `workers = 1`. @@ -37,7 +46,8 @@ SEQ-версия использует алгоритм «строка на ма ## 7. Results and Discussion ### 7.1 Correctness -Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с эталонными результатами для единичных и прямоугольных матриц с точностью $10^{-10}$. +Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с +эталонными результатами для единичных и прямоугольных матриц с точностью $10^{-10}$. ### 7.2 Performance Mode | Count | Time, s | Speedup | Efficiency @@ -45,7 +55,8 @@ Mode | Count | Time, s | Speedup | Efficiency seq | 1 | 0.852412 | 1.00 | N/A ## 8. Conclusions -Последовательная версия обеспечивает корректное перемножение разреженных матриц. Она является baseline для оценки эффективности многопоточных версий. +Последовательная версия обеспечивает корректное перемножение разреженных матриц. +Она является baseline для оценки эффективности многопоточных версий. ## 9. References 1. Microsoft MPI Documentation. diff --git a/tasks/ashihmin_d_mult_matr_crs/stl/report.md b/tasks/ashihmin_d_mult_matr_crs/stl/report.md index 4171046b18..59d3f5469e 100644 --- a/tasks/ashihmin_d_mult_matr_crs/stl/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/stl/report.md @@ -5,32 +5,46 @@ * Variant: 4 ## 1. Introduction -STL-версия использует стандартные механизмы многопоточности C++, такие как `std::async` и `std::future`. Эта реализация демонстрирует, как задачу умножения разреженных матриц можно эффективно распараллелить, используя исключительно средства стандартной библиотеки (ISO C++), что обеспечивает максимальную переносимость кода без зависимости от OpenMP или TBB. +STL-версия использует стандартные механизмы многопоточности C++, такие как +`std::async` и `std::future`. Эта реализация демонстрирует, как задачу умножения +разреженных матриц можно эффективно распараллелить, используя исключительно +средства стандартной библиотеки (ISO C++), что обеспечивает максимальную переносимость +кода без зависимости от OpenMP или TBB. ## 2. Problem Statement -Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). +Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, +представленных в строковом формате (CRS). Вход: InType = std::pair. Выход: OutType = CRSMatrix, результат C = A * B. Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. ## 3. Baseline Algorithm (Sequential) -Baseline описан в seq/report.md. Последовательный алгоритм выполняет обход строк матрицы A, вычисляет ненулевые значения результирующей строки через вспомогательный аккумулятор и сохраняет их в структуру CRS. +Baseline описан в seq/report.md. Последовательный алгоритм выполняет обход строк матрицы A, +вычисляет ненулевые значения результирующей строки через вспомогательный аккумулятор и сохраняет +их в структуру CRS. ## 4. Parallelization Scheme -Для распределения нагрузки используется декомпозиция по строкам матрицы A. Весь диапазон строк делится на блоки (chunks), количество которых соответствует числу аппаратных потоков: +Для распределения нагрузки используется декомпозиция по строкам матрицы A. Весь диапазон +строк делится на блоки (chunks), количество которых соответствует числу аппаратных потоков: * **Определение потоков:** thread_count берется из `std::thread::hardware_concurrency()`. * **Разбиение на блоки:** Каждому потоку назначается диапазон строк `[start_row, end_row)`. -* **Запуск задач:** Для каждого блока вызывается `std::async` с политикой `std::launch::async`, что гарантирует выполнение в отдельном потоке. -* **Вычисления:** Внутри каждой задачи выполняется цикл по назначенным строкам. Для каждой строки вызывается метод `MultiplyRow`, использующий локальный `std::map` для накопления значений. -* **Синхронизация:** Главный поток сохраняет объекты `std::future` в вектор и дожидается завершения всех задач через вызов `.get()`. -* **Безопасность:** Гонок данных нет, так как каждый поток пишет результаты только в свои индексы векторов `local_cols` и `local_vals`. +* **Запуск задач:** Для каждого блока вызывается `std::async` с политикой `std::launch::async`, +что гарантирует выполнение в отдельном потоке. +* **Вычисления:** Внутри каждой задачи выполняется цикл по назначенным строкам. +Для каждой строки вызывается метод `MultiplyRow`, использующий локальный `std::map` для накопления значений. +* **Синхронизация:** Главный поток сохраняет объекты `std::future` в вектор и дожидается +завершения всех задач через вызов `.get()`. +* **Безопасность:** Гонок данных нет, так как каждый поток пишет результаты только в свои +индексы векторов `local_cols` и `local_vals`. ## 5. Implementation Details * Файлы: stl/include/ops_stl.hpp, stl/src/ops_stl.cpp. * Класс: AshihminDMultMatrCrsSTL. -* **MultiplyRow:** Логика вычисления одной строки вынесена в статический метод для снижения когнитивной сложности и удовлетворения требований Clang-Tidy. -* **Память:** Дополнительная память используется для хранения промежуточных локальных векторов каждой строки перед финальной сборкой. +* **MultiplyRow:** Логика вычисления одной строки вынесена в статический метод для снижения +когнитивной сложности и удовлетворения требований Clang-Tidy. +* **Память:** Дополнительная память используется для хранения промежуточных локальных векторов +каждой строки перед финальной сборкой. ## 6. Experimental Setup Аппаратное обеспечение: @@ -44,7 +58,9 @@ Baseline описан в seq/report.md. Последовательный алг ## 7. Results and Discussion ### 7.1 Correctness -Корректность проверялась функциональными тестами из tests/functional/main.cpp. Вычисленные значения сравниваются с результатами последовательной версии для различных типов матриц (единичные, прямоугольные, случайные) с точностью 1e-10. +Корректность проверялась функциональными тестами из tests/functional/main.cpp. +Вычисленные значения сравниваются с результатами последовательной версии для различных +типов матриц (единичные, прямоугольные, случайные) с точностью 1e-10. ### 7.2 Performance Mode | Count | Time, s | Speedup | Efficiency @@ -55,7 +71,9 @@ stl | 4 | 0.247794 | 3.44 | 86.00% stl | 6 | 0.181245 | 4.70 | 78.33% ## 8. Conclusions -Реализация на базе STL (`std::async`) показала производительность, сопоставимую с OpenMP. Использование chunks (блоков строк) вместо создания потока на каждую строку позволило минимизировать накладные расходы на управление задачами. +Реализация на базе STL (`std::async`) показала производительность, сопоставимую с +OpenMP. Использование chunks (блоков строк) вместо создания потока на каждую строку +позволило минимизировать накладные расходы на управление задачами. ## 9. References 1. ISO C++ Standard Library Documentation: std::thread, std::async. diff --git a/tasks/ashihmin_d_mult_matr_crs/tbb/report.md b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md index 0534b2aacf..d24fca9acf 100644 --- a/tasks/ashihmin_d_mult_matr_crs/tbb/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md @@ -5,32 +5,43 @@ * Variant: 4 ## 1. Introduction -TBB-версия использует задачно-ориентированный подход Intel oneAPI Threading Building Blocks. Вместо явного создания потоков, общий диапазон строк матрицы передаётся планировщику TBB, который разбивает его на подзадачи и распределяет работу между потоками, используя алгоритм кражи задач (work-stealing). +TBB-версия использует задачно-ориентированный подход Intel oneAPI Threading +Building Blocks. Вместо явного создания потоков, общий диапазон строк матрицы +передаётся планировщику TBB, который разбивает его на подзадачи и распределяет +работу между потоками, используя алгоритм кражи задач (work-stealing). ## 2. Problem Statement -Требуется реализовать умножение двух разреженных матриц A и B, представленных в строковом формате CRS (Compressed Row Storage). +Требуется реализовать умножение двух разреженных матриц A и B, представленных в +строковом формате CRS (Compressed Row Storage). Вход: InType = std::pair. Выход: OutType = CRSMatrix, произведение C = A * B. Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. ## 3. Baseline Algorithm (Sequential) -Baseline описан в seq/report.md. Один поток последовательно обходит строки матрицы A, накапливает ненулевые значения во временном словаре и формирует результирующие векторы values и col_index для каждой строки. +Baseline описан в seq/report.md. Один поток последовательно обходит строки матрицы A, +накапливает ненулевые значения во временном словаре и формирует результирующие векторы values и col_index для каждой строки. ## 4. Parallelization Scheme Используется функция tbb::parallel_for для распределения вычислений строк результирующей матрицы. * **Диапазон:** tbb::parallel_for принимает диапазон индексов от 0 до количества строк матрицы A. -* **Планировщик:** Используется стандартный механизм TBB. Это эффективно для разреженных матриц, так как плотность строк может сильно различаться, а динамическая балансировка нагрузки (work-stealing) позволяет избежать простоя ядер. -* **Локальность данных:** Каждый рабочий поток внутри лямбда-выражения создает свой std::map row_accumulator. Это обеспечивает потокобезопасность без использования блокировок. -* **Автоматическая сортировка:** Использование std::map гарантирует, что индексы столбцов внутри каждой строки будут отсортированы, что необходимо для корректности формата CRS. -* **Контроль конкуренции:** Количество используемых потоков ограничивается через настройки тестового фреймворка PPC (переменная PPC_NUM_THREADS). +* **Планировщик:** Используется стандартный механизм TBB. Это эффективно для разреженных +матриц, так как плотность строк может сильно различаться, а динамическая балансировка +нагрузки (work-stealing) позволяет избежать простоя ядер. +* **Локальность данных:** Каждый рабочий поток внутри лямбда-выражения создает свой +std::map row_accumulator. Это обеспечивает потокобезопасность без использования блокировок. +* **Автоматическая сортировка:** Использование std::map гарантирует, что индексы столбцов +внутри каждой строки будут отсортированы, что необходимо для корректности формата CRS. +* **Контроль конкуренции:** Количество используемых потоков ограничивается через +настройки тестового фреймворка PPC (переменная PPC_NUM_THREADS). ## 5. Implementation Details * Файлы: tbb/include/ops_tbb.hpp, tbb/src/ops_tbb.cpp. * Класс: AshihminDMultMatrCrsTBB. * Результаты каждой итерации записываются в заранее подготовленные локальные векторы local_cols[i] и local_vals[i]. * Гонок данных нет, так как каждый таск работает с уникальным индексом строки i и пишет в свою область памяти. -* Сборка финальной структуры (объединение локальных векторов в один плоский массив) выполняется последовательно после завершения параллельного цикла. +* Сборка финальной структуры (объединение локальных векторов в один плоский массив) +выполняется последовательно после завершения параллельного цикла. ## 6. Experimental Setup Аппаратное обеспечение: @@ -45,7 +56,8 @@ Baseline описан в seq/report.md. Один поток последоват ## 7. Results and Discussion 7.1 Correctness -Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с результатами последовательной версии для различных конфигураций: единичные, диагональные и прямоугольные матрицы. +Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с +результатами последовательной версии для различных конфигураций: единичные, диагональные и прямоугольные матрицы. 7.2 Performance Mode | Count | Time, s | Speedup | Efficiency @@ -56,7 +68,9 @@ tbb | 4 | 0.226705 | 3.76 | 94.0% tbb | 6 | 0.165214 | 5.16 | 86.0% ## 8. Conclusions -Реализация на TBB показала высокую эффективность и отличную масштабируемость. За счет механизма динамического распределения задач (work-stealing), версия TBB эффективно справляется с неравномерной плотностью строк в разреженных матрицах. +Реализация на TBB показала высокую эффективность и отличную масштабируемость. +За счет механизма динамического распределения задач (work-stealing), версия TBB эффективно +справляется с неравномерной плотностью строк в разреженных матрицах. ## 9. References 1. oneAPI Threading Building Blocks Documentation. From 24964d3e64835ac2b4b53731c9528c48de8e0398 Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 16:30:19 +0300 Subject: [PATCH 6/7] report + all fix --- tasks/ashihmin_d_mult_matr_crs/all/report.md | 20 ++++- tasks/ashihmin_d_mult_matr_crs/omp/report.md | 77 +++++++++++++------- tasks/ashihmin_d_mult_matr_crs/report.md | 30 +++++++- tasks/ashihmin_d_mult_matr_crs/seq/report.md | 24 +++++- tasks/ashihmin_d_mult_matr_crs/stl/report.md | 19 ++++- tasks/ashihmin_d_mult_matr_crs/tbb/report.md | 18 ++++- 6 files changed, 148 insertions(+), 40 deletions(-) diff --git a/tasks/ashihmin_d_mult_matr_crs/all/report.md b/tasks/ashihmin_d_mult_matr_crs/all/report.md index b5e584e986..b017cc194b 100644 --- a/tasks/ashihmin_d_mult_matr_crs/all/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/all/report.md @@ -5,11 +5,14 @@ * Variant: 4 ## 1. Introduction + ALL-версия представляет собой гибридную реализацию, объединяющую несколько технологий -параллелизма. Цель этой реализации — распределить вычисления между узлами кластера (процессами MPI), а внутри каждого процесса максимально +параллелизма. Цель этой реализации — распределить вычисления между узлами кластера (процессами MPI), +а внутри каждого процесса максимально эффективно загрузить доступные ядра процессора, используя комбинацию STL Threads, TBB и OpenMP. ## 2. Problem Statement + Задача: реализовать умножение двух разреженных матриц A и B в формате CRS (Compressed Row Storage). Вход: InType = std::pair. @@ -18,11 +21,13 @@ ALL-версия представляет собой гибридную реал общую память (Threads). ## 3. Baseline Algorithm (Sequential) + Baseline описан в seq/report.md. Он выполняет последовательный проход по всем строкам матрицы A. Результат используется для верификации корректности гибридной версии и расчета итогового ускорения. ## 4. Parallelization Scheme + ALL-реализация использует многоуровневую гибридную схему: 1. **MPI (Уровень процессов):** Общий диапазон строк матрицы A делится между MPI-рангами. @@ -41,6 +46,7 @@ ALL-реализация использует многоуровневую ги Конфигурация задается как workers = ranks × threads. ## 5. Implementation Details + * Файлы: all/include/ops_all.hpp, all/src/ops_all.cpp. * Класс: AshihminDMultMatrCrsALL. * **MPI_Allgatherv:** Применяется для обмена данными между процессами, так как результирующие @@ -51,22 +57,28 @@ ALL-реализация использует многоуровневую ги алгоритмов SpGEMM на небольших кластерах. ## 6. Experimental Setup + Аппаратное обеспечение: + * CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер) * RAM: 16 ГБ * OS: Windows 11 / Linux (CI) * MPI: MS-MPI / OpenMPI Окружение: + * PPC_NUM_THREADS: количество потоков внутри процесса. * PPC_NUM_PROC: количество MPI-процессов. ## 7. Results and Discussion + ### 7.1 Correctness + Корректность проверялась функциональными тестами. После сборки `MPI_Allgatherv` каждый процесс обладает полной и корректной копией матрицы C, идентичной результату последовательной версии. ### 7.2 Performance + Mode | Count (R x T) | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- seq | 1 | 0.852412 | 1.00 | N/A @@ -75,13 +87,15 @@ all | 2 x 3 | 0.192451 | 4.43 | 73.83% all | 3 x 2 | 0.198523 | 4.29 | 71.50% ## 8. Conclusions -Гибридная реализация демонстрирует возможность масштабирования алгоритма умножения CRS матриц. + +Гибридная реализация демонстрирует возможность масштабирования алгоритма умножения CRS матриц. Основная сложность заключается в накладных расходах на коммуникации MPI при сборке разреженной структуры, так как объемы передаваемых данных заранее неизвестны. Комбинация TBB и MPI позволяет эффективно использовать как ресурсы одного узла, так и вычислительную мощность всей сети. ## 9. References + 1. OpenMP Architecture Review Board. OpenMP API. 2. oneAPI Threading Building Blocks Documentation. 3. Microsoft MPI / MPICH Documentation. -4. ISO C++ Standard Library: std::thread. \ No newline at end of file +4. ISO C++ Standard Library: std::thread. diff --git a/tasks/ashihmin_d_mult_matr_crs/omp/report.md b/tasks/ashihmin_d_mult_matr_crs/omp/report.md index ebe9dbaabc..7765ea3de3 100644 --- a/tasks/ashihmin_d_mult_matr_crs/omp/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/omp/report.md @@ -5,12 +5,14 @@ * Variant: 4 ## 1. Introduction + OpenMP-версия реализует параллельное вычисление строк результирующей матрицы в общей памяти. Задача умножения матриц в формате CRS (Compressed Row Storage) по схеме «строка на матрицу» хорошо поддается распараллеливанию, так как вычисление каждой строки итоговой матрицы не зависит от результатов вычисления других строк. ## 2. Problem Statement + Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). Вход: `InType = std::pair`. @@ -18,56 +20,72 @@ A и B, представленных в строковом формате (CRS). Ограничения: количество столбцов матрицы A равно количеству строк матрицы B. ## 3. Baseline Algorithm (Sequential) + Baseline описан в `seq/report.md`. Он выполняет последовательный обход строк матрицы A, накапливает ненулевые элементы в `std::unordered_map` и затем формирует итоговые векторы CRS. ## 4. Parallelization Scheme + В OMP-версии параллелится внешний цикл по строкам матрицы A: `#pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a)` -* **shared**: `matrix_a`, `matrix_b` — исходные данные только для чтения. `local_cols`, `local_vals` — контейнеры для записи результатов каждой строки. -* **private**: внутри каждой итерации создается `std::map row_accumulator`. Это локальный аккумулятор потока для текущей строки $i$. -* **Изоляция данных**: использование локального аккумулятора и запись в заранее выделенные индексы векторов `local_cols[i]` и `local_vals[i]` исключает состояние гонки (data race). -* **Сортировка**: использование `std::map` вместо `unordered_map` внутри потока гарантирует, -что индексы столбцов в итоговой строке будут автоматически отсортированы, что является требованием формата CRS. -* **Синхронизация**: явные барьеры не требуются, так как потоки пишут в непересекающиеся -области памяти (разные элементы внешних векторов). Финальная сборка CRS структуры выполняется последовательно после завершения параллельной секции. +* **shared**: `matrix_a`, `matrix_b` — исходные данные только для чтения. `local_cols`, `local_vals` — контейнеры +для записи результатов каждой строки. +* **private**: внутри каждой итерации создается `std::map row_accumulator`. Это локальный +аккумулятор потока для текущей строки $i$. +* **Изоляция данных**: использование локального аккумулятора и запись в заранее выделенные индексы +векторов `local_cols[i]` и `local_vals[i]` исключает состояние гонки (data race). +* **Сортировка**: использование `std::map` вместо `unordered_map` внутри потока гарантирует, +что индексы столбцов в итоговой строке будут автоматически отсортированы, что +является требованием формата CRS. +* **Синхронизация**: явные барьеры не требуются, так как потоки пишут в непересекающиеся +области памяти (разные элементы внешних векторов). +Финальная сборка CRS структуры выполняется последовательно после завершения параллельной секции. ## 5. Implementation Details -* Файлы: `omp/include/ops_omp.hpp`, `omp/src/ops_omp.cpp`. -* Класс: `AshihminDMultMatrCrsOMP`. -* Память: для каждого потока динамически выделяется память под `std::map` и временные + +* Файлы: `omp/include/ops_omp.hpp`, `omp/src/ops_omp.cpp`. +* Класс: `AshihminDMultMatrCrsOMP`. +* Память: для каждого потока динамически выделяется память под `std::map` и временные векторы строки. Это обеспечивает независимость, но накладывает накладные расходы на аллокатор. -* Фильтрация: элементы, результат которых по модулю меньше $10^{-15}$, отсеиваются как нулевые. +* Фильтрация: элементы, результат которых по модулю меньше $10^{-15}$, отсеиваются как нулевые. ## 6. Experimental Setup + Аппаратное обеспечение: -* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) -* RAM: 16 ГБ -* OS: Windows 11 / Linux (CI) + +* CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) +* RAM: 16 ГБ +* OS: Windows 11 / Linux (CI) Инструменты: -* Сборка: CMake -* Компилятор: GCC / Clang (с поддержкой `-fopenmp`) -* Конфигурация: Release + +* Сборка: CMake +* Компилятор: GCC / Clang (с поддержкой `-fopenmp`) +* Конфигурация: Release Генерация данных: -* Для performance-теста используется ленточная матрица размера 40,000x40,000. -* Ширина ленты (bandwidth): 30. + +* Для performance-теста используется ленточная матрица размера 40,000x40,000. +* Ширина ленты (bandwidth): 30. ## 7. Results and Discussion + ### 7.1 Correctness + Корректность проверялась функциональными тестами из `tests/functional/main.cpp`. Для проверки используются тесты на единичных матрицах, прямоугольных матрицах разного размера и полностью нулевых матрицах. Сравнение выполняется с точностью $10^{-10}$. ### 7.2 Performance + Используемые обозначения: -* time — время выполнения performance-теста; -* speedup = time_seq / time_mode; -* efficiency = speedup / workers; -* workers — количество потоков (OMP_NUM_THREADS). + +* time — время выполнения performance-теста; +* speedup = time_seq / time_mode; +* efficiency = speedup / workers; +* workers — количество потоков (OMP_NUM_THREADS). Mode | Count | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- @@ -77,18 +95,21 @@ omp | 4 | 0.236781 | 3.60 | 90.0% omp | 6 | 0.174125 | 4.89 | 81.5% ## 8. Conclusions + Реализация эффективно использует возможности многоядерных процессоров через OpenMP. Достигнуто значительное ускорение (почти 5-кратное на 6 ядрах). Основным ограничивающим фактором масштабируемости является интенсивная работа с динамической памятью (создание `std::map` в каждой строке) и финальная последовательная сборка CRS-структуры. ## 9. References -* OpenMP Architecture Review Board. OpenMP Application Programming Interface. -* oneAPI Threading Building Blocks Documentation. -* Microsoft MPI Documentation. -* ISO C++ Standard Library Documentation: std::thread. + +* OpenMP Architecture Review Board. OpenMP Application Programming Interface. +* oneAPI Threading Building Blocks Documentation. +* Microsoft MPI Documentation. +* ISO C++ Standard Library Documentation: std::thread. ## Appendix (Optional) + Основной фрагмент RunImpl(): ```cpp @@ -113,4 +134,4 @@ bool AshihminDMultMatrCrsOMP::RunImpl() { } // ... postprocessing/assembly ... return true; -} \ No newline at end of file +} diff --git a/tasks/ashihmin_d_mult_matr_crs/report.md b/tasks/ashihmin_d_mult_matr_crs/report.md index 9317728c0d..78c4499d6f 100644 --- a/tasks/ashihmin_d_mult_matr_crs/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/report.md @@ -5,6 +5,7 @@ * Variant: 4 ## 1. Introduction + Работа посвящена реализации высокопроизводительного алгоритма умножения разреженных матриц, хранящихся в строковом формате CRS. Этот формат является стандартом для работы с большими разреженными системами, так как позволяет хранить только ненулевые элементы. В работе реализованы @@ -15,7 +16,9 @@ OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). технологий параллелизма при масштабировании на 6-ядерном процессоре. ## 2. Problem Statement + Входные данные задаются типом `InType = std::pair`: + * matrix_a — левый операнд; * matrix_b — правый операнд. @@ -26,8 +29,10 @@ OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). равно числу строк матрицы B (`matrix_a.cols == matrix_b.rows`). Элементы имеют тип `double`. ## 3. Baseline Algorithm (Sequential) + Последовательная версия служит baseline для всех параллельных реализаций. Алгоритм реализует схему «строка на матрицу». Для каждой строки $i$ матрицы A: + 1. Используется временный аккумулятор `std::unordered_map` для накопления значений. 2. Просматриваются ненулевые элементы $A_{ik}$. 3. Для каждого $A_{ik}$ выбирается соответствующая строка $k$ матрицы B. @@ -37,6 +42,7 @@ OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). в итоговую структуру. Время выполнения SEQ считается базовым (speedup = 1.0). ## 4. Parallelization Scheme + * **SEQ**: один поток, параллелизм не используется. * **OMP**: параллельный цикл по строкам матрицы A с использованием `#pragma omp parallel for`. Каждый поток имеет локальный аккумулятор `std::map`. @@ -49,7 +55,9 @@ OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). метаданных. Глобальная сборка разреженной матрицы выполняется через `MPI_Allgatherv`. ## 5. Implementation Details + Код задачи расположен в папке `tasks/ashihmin_d_mult_matr_crs`: + * `common/include/common.hpp` — структуры данных и типы; * `seq/src/ops_seq.cpp` — последовательный baseline; * `omp/src/ops_omp.cpp` — OpenMP реализация; @@ -62,30 +70,38 @@ OpenMP, TBB, STL и гибридной схемы ALL (MPI + Threads). автоматическую сортировку столбцов согласно спецификации CRS. ## 6. Experimental Setup + Аппаратное обеспечение: + * CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) * RAM: 16 ГБ * OS: Windows 11 Pro / Linux (CI) Инструменты: + * Сборка: CMake * Компилятор: GCC / Clang / MSVC * Конфигурация: Release Окружение: + * `PPC_NUM_THREADS`: задает число потоков (1–6). * `PPC_NUM_PROC`: задает число MPI-процессов для ALL. Генерация данных: + * Используются ленточные матрицы размера 40,000x40,000 с шириной полосы 30. ## 7. Results and Discussion + ### 7.1 Correctness + Корректность проверялась функциональными тестами. Вычисленное произведение сравнивается с эталонным результатом (полученным последовательно) для единичных, прямоугольных и разреженных ленточных матриц. Допустимая погрешность: $10^{-10}$. ### 7.2 Performance + Mode | Count | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- seq | 1 | 0.852412 | 1.00 | N/A @@ -95,21 +111,25 @@ stl | 6 | 0.181245 | 4.70 | 78.33% all | 2 x 3 | 0.192451 | 4.43 | 73.83% ## 8. Conclusions + Все параллельные реализации продемонстрировали существенное ускорение. Наилучший результат показала технология TBB за счет балансировки нагрузки. Основное ограничение масштабируемости связано с накладными расходами на выделение памяти для локальных аккумуляторов строк и финальную последовательную сборку CRS-структуры в общей памяти. ## 9. References + * OpenMP Architecture Review Board. OpenMP Application Programming Interface. * oneAPI Threading Building Blocks Documentation. * Microsoft MPI Documentation. * ISO C++ Standard Library Documentation: std::thread, std::async. ## Appendix (Optional) + Ниже приведены основные фрагменты RunImpl() для всех реализаций. ### SEQ RunImpl + ```cpp for (int row_index = 0; row_index < matrix_a.rows; ++row_index) { std::unordered_map accumulator; @@ -122,7 +142,9 @@ for (int row_index = 0; row_index < matrix_a.rows; ++row_index) { } } ``` + ### OMP RunImpl + ```cpp #pragma omp parallel for default(none) shared(matrix_a, matrix_b, local_cols, local_vals, rows_a) for (int i = 0; i < rows_a; ++i) { @@ -135,7 +157,9 @@ for (int i = 0; i < rows_a; ++i) { } } ``` + ### TBB RunImpl + ```cpp tbb::parallel_for(0, rows_a, [&](int i) { std::map row_accumulator; @@ -147,7 +171,9 @@ tbb::parallel_for(0, rows_a, [&](int i) { } }); ``` + ### STL RunImpl + ```cpp for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { futures.push_back(std::async(std::launch::async, [=, &matrix_a, &matrix_b, &local_cols, &local_vals] { @@ -157,7 +183,9 @@ for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { })); } ``` + ### ALL RunImpl + ```cpp MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); @@ -167,4 +195,4 @@ for (int t = 0; t < thread_count; ++t) { } for (auto &th : threads) th.join(); // Collect results using MPI_Allgatherv -MPI_Allgatherv(my_flat_cols.data(), ..., matrix_c.col_index.data(), ...); \ No newline at end of file +MPI_Allgatherv(my_flat_cols.data(), ..., matrix_c.col_index.data(), ...); diff --git a/tasks/ashihmin_d_mult_matr_crs/seq/report.md b/tasks/ashihmin_d_mult_matr_crs/seq/report.md index dff745fb3b..76c012995a 100644 --- a/tasks/ashihmin_d_mult_matr_crs/seq/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/seq/report.md @@ -5,66 +5,82 @@ * Variant: 4 ## 1. Introduction + Последовательная версия используется как базовая линия производительности. Она реализует классический алгоритм умножения разреженных матриц в формате CRS, который служит эталоном для проверки корректности результатов параллельных реализаций и расчета ускорения. ## 2. Problem Statement -Вход: `InType = std::pair`, где каждая матрица представлена + +Вход: `InType = std::pair`, где каждая матрица представлена структурой с полями `rows`, `cols`, `row_ptr`, `col_index`, `values`. Выход: `OutType = CRSMatrix`, результат умножения матриц A и B. Ограничения: `matrix_a.cols == matrix_b.rows`, элементы типа `double`. ## 3. Baseline Algorithm (Sequential) + SEQ-версия использует алгоритм «строка на матрицу». Для каждой строки $i$ матрицы A создается временный аккумулятор (`std::unordered_map`). Алгоритм проходит по всем ненулевым элементам строки $i$ матрицы A. Для каждого элемента $A_{ik}$ находится соответствующая строка $k$ матрицы B, и её значения, умноженные на $A_{ik}$, добавляются -в аккумулятор. +в аккумулятор. После завершения обработки строки, данные из аккумулятора сортируются по индексам столбцов, фильтруются от нулевых значений и записываются в итоговую структуру CRS. ## 4. Parallelization Scheme + Параллелизм отсутствует. Все операции выполняются последовательно в одном потоке. `workers = 1`. ## 5. Implementation Details + * Файлы: `seq/include/ops_seq.hpp`, `seq/src/ops_seq.cpp`. * Класс: `AshihminDMultMatrCrsSEQ`. * `ValidationImpl()`: Проверяет совместимость матриц по размерности. -* `RunImpl()`: Реализует основной расчет с использованием `std::unordered_map` для накопления строк и `std::ranges::sort` для упорядочивания столбцов. +* `RunImpl()`: Реализует основной расчет с использованием `std::unordered_map` для накопления строк и +`std::ranges::sort` для упорядочивания столбцов. ## 6. Experimental Setup + Аппаратное обеспечение: + * CPU: 12th Gen Intel(R) Core(TM) i5-12450H (8 ядер / 12 потоков) * RAM: 16 ГБ * OS: Windows 11 / Linux (CI) Генерация данных: + * Для performance-теста используется ленточная матрица размера 40,000x40,000. * Ширина ленты (bandwidth): 30. ## 7. Results and Discussion + ### 7.1 Correctness + Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с эталонными результатами для единичных и прямоугольных матриц с точностью $10^{-10}$. ### 7.2 Performance + Mode | Count | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- seq | 1 | 0.852412 | 1.00 | N/A ## 8. Conclusions + Последовательная версия обеспечивает корректное перемножение разреженных матриц. Она является baseline для оценки эффективности многопоточных версий. ## 9. References + 1. Microsoft MPI Documentation. 2. ISO C++ Standard Library Documentation: std::thread. 3. Sparse Matrix-Matrix Multiplication Algorithms. ## Appendix (Optional) + Основной фрагмент RunImpl(): + ```cpp bool AshihminDMultMatrCrsSEQ::RunImpl() { const auto &matrix_a = GetInput().first; @@ -110,4 +126,4 @@ bool AshihminDMultMatrCrsSEQ::RunImpl() { } return true; -} \ No newline at end of file +} diff --git a/tasks/ashihmin_d_mult_matr_crs/stl/report.md b/tasks/ashihmin_d_mult_matr_crs/stl/report.md index 59d3f5469e..1584ba6404 100644 --- a/tasks/ashihmin_d_mult_matr_crs/stl/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/stl/report.md @@ -5,6 +5,7 @@ * Variant: 4 ## 1. Introduction + STL-версия использует стандартные механизмы многопоточности C++, такие как `std::async` и `std::future`. Эта реализация демонстрирует, как задачу умножения разреженных матриц можно эффективно распараллелить, используя исключительно @@ -12,6 +13,7 @@ STL-версия использует стандартные механизмы кода без зависимости от OpenMP или TBB. ## 2. Problem Statement + Задача совпадает с SEQ-версией: реализовать умножение двух разреженных матриц A и B, представленных в строковом формате (CRS). Вход: InType = std::pair. @@ -19,11 +21,13 @@ STL-версия использует стандартные механизмы Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. ## 3. Baseline Algorithm (Sequential) + Baseline описан в seq/report.md. Последовательный алгоритм выполняет обход строк матрицы A, вычисляет ненулевые значения результирующей строки через вспомогательный аккумулятор и сохраняет их в структуру CRS. ## 4. Parallelization Scheme + Для распределения нагрузки используется декомпозиция по строкам матрицы A. Весь диапазон строк делится на блоки (chunks), количество которых соответствует числу аппаратных потоков: @@ -34,11 +38,12 @@ Baseline описан в seq/report.md. Последовательный алг * **Вычисления:** Внутри каждой задачи выполняется цикл по назначенным строкам. Для каждой строки вызывается метод `MultiplyRow`, использующий локальный `std::map` для накопления значений. * **Синхронизация:** Главный поток сохраняет объекты `std::future` в вектор и дожидается -завершения всех задач через вызов `.get()`. +завершения всех задач через вызов `.get()`. * **Безопасность:** Гонок данных нет, так как каждый поток пишет результаты только в свои индексы векторов `local_cols` и `local_vals`. ## 5. Implementation Details + * Файлы: stl/include/ops_stl.hpp, stl/src/ops_stl.cpp. * Класс: AshihminDMultMatrCrsSTL. * **MultiplyRow:** Логика вычисления одной строки вынесена в статический метод для снижения @@ -47,22 +52,28 @@ Baseline описан в seq/report.md. Последовательный алг каждой строки перед финальной сборкой. ## 6. Experimental Setup + Аппаратное обеспечение: + * CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер / 6 потоков) * RAM: 16 ГБ * OS: Windows 11 / Linux (CI) Инструменты: + * Сборка: CMake * Компилятор: GCC / Clang / MSVC (стандарт C++20) ## 7. Results and Discussion + ### 7.1 Correctness + Корректность проверялась функциональными тестами из tests/functional/main.cpp. Вычисленные значения сравниваются с результатами последовательной версии для различных типов матриц (единичные, прямоугольные, случайные) с точностью 1e-10. ### 7.2 Performance + Mode | Count | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- seq | 1 | 0.852412 | 1.00 | N/A @@ -71,18 +82,22 @@ stl | 4 | 0.247794 | 3.44 | 86.00% stl | 6 | 0.181245 | 4.70 | 78.33% ## 8. Conclusions + Реализация на базе STL (`std::async`) показала производительность, сопоставимую с OpenMP. Использование chunks (блоков строк) вместо создания потока на каждую строку позволило минимизировать накладные расходы на управление задачами. ## 9. References + 1. ISO C++ Standard Library Documentation: std::thread, std::async. 2. Anthony Williams. C++ Concurrency in Action. 3. OpenMP Architecture Review Board. OpenMP API. 4. Microsoft MPI Documentation. ## Appendix (Optional) + Фрагмент RunImpl() с использованием std::async: + ```cpp for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { int start_row = thread_idx * chunk_size; @@ -95,4 +110,4 @@ for (int thread_idx = 0; thread_idx < num_threads; ++thread_idx) { } })); } -for (auto &fut : futures) fut.get(); \ No newline at end of file +for (auto &fut : futures) fut.get(); diff --git a/tasks/ashihmin_d_mult_matr_crs/tbb/report.md b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md index d24fca9acf..9b4c3690e5 100644 --- a/tasks/ashihmin_d_mult_matr_crs/tbb/report.md +++ b/tasks/ashihmin_d_mult_matr_crs/tbb/report.md @@ -5,23 +5,27 @@ * Variant: 4 ## 1. Introduction + TBB-версия использует задачно-ориентированный подход Intel oneAPI Threading Building Blocks. Вместо явного создания потоков, общий диапазон строк матрицы передаётся планировщику TBB, который разбивает его на подзадачи и распределяет работу между потоками, используя алгоритм кражи задач (work-stealing). ## 2. Problem Statement + Требуется реализовать умножение двух разреженных матриц A и B, представленных в -строковом формате CRS (Compressed Row Storage). +строковом формате CRS (Compressed Row Storage). Вход: InType = std::pair. Выход: OutType = CRSMatrix, произведение C = A * B. Ограничения: matrix_a.cols == matrix_b.rows, элементы типа double. ## 3. Baseline Algorithm (Sequential) + Baseline описан в seq/report.md. Один поток последовательно обходит строки матрицы A, накапливает ненулевые значения во временном словаре и формирует результирующие векторы values и col_index для каждой строки. ## 4. Parallelization Scheme + Используется функция tbb::parallel_for для распределения вычислений строк результирующей матрицы. * **Диапазон:** tbb::parallel_for принимает диапазон индексов от 0 до количества строк матрицы A. @@ -36,6 +40,7 @@ std::map row_accumulator. Это обеспечивает пото настройки тестового фреймворка PPC (переменная PPC_NUM_THREADS). ## 5. Implementation Details + * Файлы: tbb/include/ops_tbb.hpp, tbb/src/ops_tbb.cpp. * Класс: AshihminDMultMatrCrsTBB. * Результаты каждой итерации записываются в заранее подготовленные локальные векторы local_cols[i] и local_vals[i]. @@ -44,22 +49,27 @@ std::map row_accumulator. Это обеспечивает пото выполняется последовательно после завершения параллельного цикла. ## 6. Experimental Setup + Аппаратное обеспечение: + * CPU: AMD Ryzen 5 3500X (3.60 GHz, 6 ядер) * RAM: 16 ГБ * OS: Windows 11 / Linux (CI) Инструменты: + * Сборка: CMake * Компилятор: GCC / Clang / MSVC * Библиотека: Intel oneAPI TBB ## 7. Results and Discussion + 7.1 Correctness Корректность проверялась функциональными тестами. Вычисленные значения сравниваются с результатами последовательной версии для различных конфигураций: единичные, диагональные и прямоугольные матрицы. 7.2 Performance + Mode | Count | Time, s | Speedup | Efficiency --- | --- | --- | --- | --- seq | 1 | 0.852412 | 1.00 | N/A @@ -68,18 +78,22 @@ tbb | 4 | 0.226705 | 3.76 | 94.0% tbb | 6 | 0.165214 | 5.16 | 86.0% ## 8. Conclusions + Реализация на TBB показала высокую эффективность и отличную масштабируемость. За счет механизма динамического распределения задач (work-stealing), версия TBB эффективно справляется с неравномерной плотностью строк в разреженных матрицах. ## 9. References + 1. oneAPI Threading Building Blocks Documentation. 2. OpenMP Architecture Review Board. OpenMP API. 3. Microsoft MPI Documentation. 4. ISO C++ Standard Library Documentation: std::thread. ## Appendix (Optional) + Основной фрагмент RunImpl(): + ```cpp tbb::parallel_for(0, rows_a, [&](int i) { std::map row_accumulator; @@ -91,4 +105,4 @@ tbb::parallel_for(0, rows_a, [&](int i) { } } // Filter and save... -}); \ No newline at end of file +}); From ce9995d4fc2608707b498f9ccfb0c2cafe0bdb3e Mon Sep 17 00:00:00 2001 From: DaniilAsh Date: Thu, 4 Jun 2026 19:08:45 +0300 Subject: [PATCH 7/7] report + all fix --- .../all/include/ops_all.hpp | 5 ++ .../all/src/ops_all.cpp | 66 ++++++++++--------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp b/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp index 141a543a87..3ccdfdbaae 100644 --- a/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp +++ b/tasks/ashihmin_d_mult_matr_crs/all/include/ops_all.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "ashihmin_d_mult_matr_crs/common/include/common.hpp" #include "task/include/task.hpp" @@ -18,6 +20,9 @@ class AshihminDMultMatrCrsALL : public BaseTask { bool PreProcessingImpl() override; bool RunImpl() override; bool PostProcessingImpl() override; + + static void MultiplyRow(int global_row_idx, int local_idx, const CRSMatrix &matrix_a, const CRSMatrix &matrix_b, + std::vector> &local_cols, std::vector> &local_vals); }; } // namespace ashihmin_d_mult_matr_crs diff --git a/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp b/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp index f3f76b2264..9158bd0b3f 100644 --- a/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp +++ b/tasks/ashihmin_d_mult_matr_crs/all/src/ops_all.cpp @@ -26,25 +26,46 @@ bool AshihminDMultMatrCrsALL::ValidationImpl() { bool AshihminDMultMatrCrsALL::PreProcessingImpl() { auto &matrix_c = GetOutput(); + matrix_c.rows = GetInput().first.rows; matrix_c.cols = GetInput().second.cols; return true; } +void AshihminDMultMatrCrsALL::MultiplyRow(int global_row_idx, int local_idx, const CRSMatrix &matrix_a, + const CRSMatrix &matrix_b, std::vector> &local_cols, + std::vector> &local_vals) { + std::map row_accumulator; + for (int j = matrix_a.row_ptr[global_row_idx]; j < matrix_a.row_ptr[global_row_idx + 1]; ++j) { + int col_a = matrix_a.col_index[j]; + double val_a = matrix_a.values[j]; + for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { + row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; + } + } + + for (const auto &entry : row_accumulator) { + if (std::abs(entry.second) > 1e-15) { + local_cols[local_idx].push_back(entry.first); + local_vals[local_idx].push_back(entry.second); + } + } +} + bool AshihminDMultMatrCrsALL::RunImpl() { const auto &matrix_a = GetInput().first; const auto &matrix_b = GetInput().second; auto &matrix_c = GetOutput(); - int rank, size; + int rank = 0; + int size = 0; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); int rows_a = matrix_a.rows; - int base_rows = rows_a / size; int rem = rows_a % size; - int my_start = rank * base_rows + std::min(rank, rem); + int my_start = (rank * base_rows) + std::min(rank, rem); int my_end = my_start + base_rows + (rank < rem ? 1 : 0); int my_row_count = my_end - my_start; @@ -55,33 +76,16 @@ bool AshihminDMultMatrCrsALL::RunImpl() { std::vector threads; auto compute_rows = [&](int start_idx, int end_idx) { - tbb::parallel_for(start_idx, end_idx, [&](int i) { - int global_row = my_start + i; - std::map row_accumulator; - - for (int j = matrix_a.row_ptr[global_row]; j < matrix_a.row_ptr[global_row + 1]; ++j) { - int col_a = matrix_a.col_index[j]; - double val_a = matrix_a.values[j]; - for (int k = matrix_b.row_ptr[col_a]; k < matrix_b.row_ptr[col_a + 1]; ++k) { - row_accumulator[matrix_b.col_index[k]] += val_a * matrix_b.values[k]; - } - } - - for (auto it = row_accumulator.begin(); it != row_accumulator.end(); ++it) { - if (std::abs(it->second) > 1e-15) { - local_cols[i].push_back(it->first); - local_vals[i].push_back(it->second); - } - } - }); + tbb::parallel_for(start_idx, end_idx, + [&](int i) { MultiplyRow(my_start + i, i, matrix_a, matrix_b, local_cols, local_vals); }); }; int stl_chunk = (my_row_count + thread_count - 1) / thread_count; - for (int t = 0; t < thread_count; ++t) { - int s = t * stl_chunk; - int e = std::min(s + stl_chunk, my_row_count); - if (s < e) { - threads.emplace_back(compute_rows, s, e); + for (int thread_idx = 0; thread_idx < thread_count; ++thread_idx) { + int start_chunk = thread_idx * stl_chunk; + int end_chunk = std::min(start_chunk + stl_chunk, my_row_count); + if (start_chunk < end_chunk) { + threads.emplace_back(compute_rows, start_chunk, end_chunk); } } for (auto &th : threads) { @@ -89,7 +93,7 @@ bool AshihminDMultMatrCrsALL::RunImpl() { } std::vector my_nnz_per_row(my_row_count); -#pragma omp parallel for +#pragma omp parallel for default(none) shared(my_nnz_per_row, local_cols, my_row_count) for (int i = 0; i < my_row_count; ++i) { my_nnz_per_row[i] = static_cast(local_cols[i].size()); } @@ -104,7 +108,6 @@ bool AshihminDMultMatrCrsALL::RunImpl() { std::vector all_nnz_per_row(rows_a); std::vector recv_counts(size); std::vector displs(size); - for (int i = 0; i < size; ++i) { recv_counts[i] = (rows_a / size) + (i < (rows_a % size) ? 1 : 0); displs[i] = (i == 0) ? 0 : displs[i - 1] + recv_counts[i - 1]; @@ -118,9 +121,8 @@ bool AshihminDMultMatrCrsALL::RunImpl() { matrix_c.row_ptr[i + 1] = matrix_c.row_ptr[i] + all_nnz_per_row[i]; } - int total_nnz = matrix_c.row_ptr[rows_a]; - matrix_c.col_index.resize(total_nnz); - matrix_c.values.resize(total_nnz); + matrix_c.col_index.resize(matrix_c.row_ptr[rows_a]); + matrix_c.values.resize(matrix_c.row_ptr[rows_a]); std::vector val_recv_counts(size); std::vector val_displs(size);