From c19306207c2d5e4e09e1594755576a376880a23e Mon Sep 17 00:00:00 2001 From: Artemi Alekseev Date: Fri, 22 May 2026 17:30:03 +0000 Subject: [PATCH] reports --- .../alekseev_a_mult_matrix_crs/all/report.md | 385 +++++++++++++++ .../alekseev_a_mult_matrix_crs/omp/report.md | 306 ++++++++++++ tasks/alekseev_a_mult_matrix_crs/report.md | 448 ++++++++++++++++++ .../alekseev_a_mult_matrix_crs/seq/report.md | 291 ++++++++++++ .../alekseev_a_mult_matrix_crs/stl/report.md | 357 ++++++++++++++ .../alekseev_a_mult_matrix_crs/tbb/report.md | 333 +++++++++++++ 6 files changed, 2120 insertions(+) create mode 100644 tasks/alekseev_a_mult_matrix_crs/all/report.md create mode 100644 tasks/alekseev_a_mult_matrix_crs/omp/report.md create mode 100644 tasks/alekseev_a_mult_matrix_crs/report.md create mode 100644 tasks/alekseev_a_mult_matrix_crs/seq/report.md create mode 100644 tasks/alekseev_a_mult_matrix_crs/stl/report.md create mode 100644 tasks/alekseev_a_mult_matrix_crs/tbb/report.md diff --git a/tasks/alekseev_a_mult_matrix_crs/all/report.md b/tasks/alekseev_a_mult_matrix_crs/all/report.md new file mode 100644 index 0000000000..e5b7315088 --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/all/report.md @@ -0,0 +1,385 @@ +# Умножение разреженных матриц. Элементы типа double. Формат хранения CRS — ALL + +- Student: Алексеев Артемий Алексеевич +- Technology: ALL +- Variant: 4 + +## 1. Контекст + +Задача — произведение двух разреженных матриц `A` и `B` +в формате хранения CRS (Compressed Row Storage). + +Для реализации используется +гибридная технология распараллеливания, +сочетающая: +- MPI — для распределения вычислений + между процессами; +- OpenMP — для многопоточной обработки + внутри каждого процесса. + +В основе вычислений лежит +алгоритм Густавсона (Gustavson SpGEMM), +адаптированный +для гибридной параллельной архитектуры. + +MPI используется +для распределения строк матрицы `A` +между процессами, +а OpenMP — +для параллельной обработки строк +внутри локального диапазона процесса. + +## 2. Постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица + в формате CRS; +- `B` — вторая разреженная матрица + в формате CRS. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — ненулевые элементы; +- `col_indices` — индексы столбцов; +- `row_ptr` — указатели начала строк; +- `rows`, `cols` — размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица `C = A·B` +в формате CRS. + +**Ограничения:** + +- размеры матриц + должны быть корректны; +- `row_ptr` + не должен быть пустым; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`; +- индексы столбцов + не должны выходить + за границы матрицы. + +**Крайние случаи:** + +- матрица `1×1`; +- нулевая матрица; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- строки без ненулевых элементов. + +## 3. Базовый алгоритм + +Используется алгоритм Густавсона +для умножения разреженных матриц. + +Для каждой строки матрицы `A` +выполняется проход +по ненулевым элементам строки. +Для каждого элемента `A(i, k)` +выбирается соответствующая строка матрицы `B`, +после чего результаты умножения +накапливаются +во временном аккумуляторе. + +```text +для каждой строки i матрицы A: + для каждого ненулевого элемента A(i, k): + для каждого элемента B(k, j): + accum[j] += A(i, k) * B(k, j) + + сохранить ненулевые accum[j] в CRS +``` + +Распараллеливание реализуется +на двух уровнях: + +1. MPI: + строки матрицы `A` + распределяются + между процессами. + +2. OpenMP: + внутри каждого MPI-процесса + строки обрабатываются параллельно + с помощью потоков. + +Распределение строк между процессами: + +```cpp +int rows_per_proc = + static_cast(a.rows) / size; +``` + +OpenMP-распараллеливание: + +```cpp +#pragma omp for schedule(dynamic) +``` + +Асимптотика по времени: + +```text +O(nnz(A) * bandwidth(B)) +``` + +В худшем случае: + +```text +O(N³) +``` + +Асимптотика по памяти: + +```text +O(M * T * P) +``` + +где: +- `M` — + количество столбцов результирующей матрицы; +- `T` — + количество потоков OpenMP; +- `P` — + количество MPI-процессов. + +Дополнительная память используется +для локальных аккумуляторов, +буферов MPI +и временных контейнеров потоков. + +## 4. Детали реализации + +**Файлы:** + +- `all/include/ops_all.hpp` +- `all/src/ops_all.cpp` + +Перед началом вычислений +матрицы распространяются +между процессами +с помощью MPI: + +```cpp +MPI_Bcast(...) +``` + +Каждый процесс получает +копии матриц `A` и `B`, +после чего вычисляет +свой диапазон строк. + +Ключевой фрагмент `RunImpl`: + +```cpp +#pragma omp parallel default(none) \ +shared(p_a, p_b, p_lv, p_lc, local_n, local_start) +{ + std::vector accum(p_b->cols, 0.0); + std::vector touched_flag(p_b->cols, -1); + std::vector touched_cols; + +#pragma omp for schedule(dynamic) + for (int i = 0; i < local_n; ++i) { + + auto g_row = + static_cast(local_start) + + static_cast(i); + + ProcessRow(g_row, + *p_a, + *p_b, + (*p_lv)[i], + (*p_lc)[i], + accum, + touched_flag, + touched_cols); + } +} +``` + +Каждый поток использует: +- локальный аккумулятор `accum`; +- локальный массив `touched_flag`; +- локальный список `touched_cols`. + +Результаты вычислений +сохраняются +во временные контейнеры: + +```cpp +std::vector> local_v(local_n); +std::vector> local_c(local_n); +``` + +После завершения вычислений +локальные результаты +собираются +с использованием: + +```cpp +MPI_Gatherv(...) +``` + +Финальная CRS-структура +формируется +на процессе с `rank = 0`, +после чего +распространяется +между всеми процессами. + +**Этапы пайплайна:** + +- `ValidationImpl` — + проверяет корректность размеров матриц + на главном процессе; +- `PreProcessingImpl` — + выполняет подготовку вычислений; +- `RunImpl` — + выполняет MPI + OpenMP умножение; +- `PostProcessingImpl` — + синхронизирует процессы + через `MPI_Barrier`. + +## 5. Проверка корректности + +Корректность гибридной версии +проверяется +функциональными тестами +из `tests/functional/main.cpp`. + +Проверяются следующие случаи: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `1×1`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- стандартные матрицы `2×2`. + +Для проверки результатов используется функция: + +```cpp +CompareCRS() +``` + +Сравнение выполняется: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Результат функциональных тестов: + +```text +[ PASSED ] 6 tests. +``` + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32,0 ГБ | +| MPI Processes | 4 | +| OpenMP Threads | 4 | +| Total Workers | 16 | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Команды сборки и запуска:** + +```bash +cmake -B build + +# Функциональные тесты +mpirun -np 4 ./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +mpirun -np 4 ./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +## 7. Результаты + +Результаты perf-тестирования +(`pipeline` и `task_run`, +режим `performance`): + +| Режим | Время (с) | Рабочих единиц | +| -------- | ---------- | -------------- | +| pipeline | 0.842731 | 16 | +| task_run | 0.691508 | 16 | + +Тестирование выполнялось +на ленточных разреженных матрицах +размера: + +```cpp +kSize = 10000 +kBandwidth = 30 +``` + +Наиболее затратной частью алгоритма +является проход +по строкам матрицы `B` +и накопление промежуточных значений +во временных аккумуляторах. + +Гибридная MPI + OpenMP реализация +показывает наилучшую производительность +среди всех реализованных технологий +за счет двухуровневого распараллеливания. + +MPI обеспечивает +эффективное распределение вычислений +между процессами, +а OpenMP — +дополнительное ускорение +внутри каждого процесса. + +## 8. Выводы + +В ходе работы была реализована +гибридная параллельная версия +алгоритма Густавсона +для умножения разреженных матриц +в формате CRS +с использованием MPI и OpenMP. + +Распределение строк матрицы +между MPI-процессами +и дополнительное OpenMP-распараллеливание +внутри процессов +позволили эффективно использовать +многоядерную архитектуру процессора. + +Использование локальных временных структур +исключило возникновение состояния гонки +при накоплении промежуточных результатов. + +Гибридная реализация +демонстрирует максимальное ускорение +по сравнению +с последовательной версией +и превосходит реализации +на STL, OpenMP и TBB +за счет комбинированного подхода +к распараллеливанию. diff --git a/tasks/alekseev_a_mult_matrix_crs/omp/report.md b/tasks/alekseev_a_mult_matrix_crs/omp/report.md new file mode 100644 index 0000000000..28f32c2407 --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/omp/report.md @@ -0,0 +1,306 @@ +# Умножение разреженных матриц. Элементы типа double. Формат хранения CRS — OMP + +- Student: Алексеев Артемий Алексеевич +- Technology: OMP +- Variant: 4 + +## 1. Контекст + +Задача — произведение двух разреженных матриц `A` и `B` +в формате хранения CRS (Compressed Row Storage). + +Для выполнения вычислений используется +алгоритм Густавсона (Gustavson SpGEMM), +адаптированный для параллельного выполнения +с использованием технологии OpenMP. + +Распараллеливание выполняется +по строкам матрицы `A`, +так как вычисления отдельных строк +результирующей матрицы независимы друг от друга. + +## 2. Постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица + в формате CRS; +- `B` — вторая разреженная матрица + в формате CRS. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — ненулевые элементы; +- `col_indices` — индексы столбцов; +- `row_ptr` — указатели начала строк; +- `rows`, `cols` — размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица `C = A·B` +в формате CRS. + +**Ограничения:** + +- количество строк и столбцов матриц + должно быть больше нуля; +- CRS-структура должна быть корректной; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`; +- индексы столбцов + не должны выходить + за границы матрицы. + +**Крайние случаи:** + +- матрица `1×1`; +- нулевая матрица; +- диагональные разреженные матрицы; +- прямоугольные матрицы; +- строки без ненулевых элементов. + +## 3. Базовый алгоритм + +Используется алгоритм Густавсона +для умножения разреженных матриц. + +Для каждой строки матрицы `A` +выполняется проход +по ненулевым элементам строки. +Для каждого элемента +выбирается соответствующая строка матрицы `B`, +после чего результаты умножения +накапливаются во временном аккумуляторе. + +```text +для каждой строки i матрицы A: + для каждого ненулевого элемента A(i, k): + для каждого элемента B(k, j): + accum[j] += A(i, k) * B(k, j) + + сохранить ненулевые accum[j] в CRS +``` + +Распараллеливание выполняется +по внешнему циклу обработки строк. + +```cpp +#pragma omp for schedule(dynamic) +``` + +Каждый поток обрабатывает +собственный набор строк, +используя локальные временные структуры. + +Асимптотика по времени: + +```text +O(nnz(A) * bandwidth(B)) +``` + +В худшем случае: + +```text +O(N³) +``` + +Асимптотика по памяти: + +```text +O(M * T) +``` + +где: +- `M` — + количество столбцов результирующей матрицы; +- `T` — + число потоков OpenMP. + +Дополнительная память используется +для локальных аккумуляторов +и временных контейнеров потоков. + +## 4. Детали реализации + +**Файлы:** + +- `omp/include/ops_omp.hpp` +- `omp/src/ops_omp.cpp` + +Ключевой фрагмент `RunImpl`: + +```cpp +#pragma omp parallel default(none) shared(a, b, c, temp_values, temp_cols, rows_limit) +{ + std::vector accum(c.cols, 0.0); + std::vector touched_flag(c.cols, -1); + std::vector touched_cols; + +#pragma omp for schedule(dynamic) + for (int i = 0; i < rows_limit; ++i) { + ProcessRow(static_cast(i), + a, + b, + temp_values[i], + temp_cols[i], + accum, + touched_flag, + touched_cols); + } +} +``` + +Для предотвращения состояния гонки +каждый поток использует: +- локальный аккумулятор `accum`; +- локальный массив `touched_flag`; +- локальный список `touched_cols`. + +Результаты вычислений +для каждой строки +сохраняются +во временные контейнеры: + +```cpp +std::vector> temp_values(c.rows); +std::vector> temp_cols(c.rows); +``` + +После завершения параллельной области +выполняется последовательная сборка +итоговой CRS-структуры. + +**Этапы пайплайна:** + +- `ValidationImpl` — + проверяет корректность размеров матриц; +- `PreProcessingImpl` — + очищает выходную структуру; +- `RunImpl` — + выполняет параллельное умножение; +- `PostProcessingImpl` — + дополнительной обработки не выполняет. + +## 5. Проверка корректности + +Корректность OMP-версии проверяется +функциональными тестами +из `tests/functional/main.cpp`. + +Используется 6 тестовых наборов: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `2×2`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- матрица `1×1`. + +Для проверки результатов используется функция: + +```cpp +CompareCRS() +``` + +Сравнение выполняется: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Результат функциональных тестов: + +```text +[ PASSED ] 30 tests. +``` + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32,0 ГБ | +| Threads | 16 | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Команды сборки и запуска:** + +```bash +cmake -B build + +# Функциональные тесты +./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +## 7. Результаты + +Результаты perf-тестирования +(`pipeline` и `task_run`, +режим `performance`): + +| Режим | Время (с) | Рабочих единиц | +| -------- | ---------- | -------------- | +| pipeline | 1.284193 | 16 | +| task_run | 1.037552 | 16 | + +Тестирование выполнялось +на ленточных разреженных матрицах +размера: + +```cpp +kSize = 10000 +kBandwidth = 30 +``` + +Наиболее затратной частью алгоритма +является обработка строк матрицы `B` +и накопление промежуточных значений +во временных аккумуляторах. + +Использование OpenMP +позволило существенно сократить +время выполнения +по сравнению с последовательной реализацией +за счет независимой обработки строк матрицы. + +## 8. Выводы + +В ходе работы была реализована +параллельная версия алгоритма Густавсона +для умножения разреженных матриц +в формате CRS +с использованием технологии OpenMP. + +Распараллеливание внешнего цикла +по строкам матрицы `A` +позволило эффективно распределить вычисления +между потоками. + +Использование локальных временных структур +исключило возникновение состояния гонки +при параллельном накоплении результатов. + +OpenMP-версия демонстрирует +существенное ускорение +относительно последовательной реализации +при сохранении корректности вычислений. diff --git a/tasks/alekseev_a_mult_matrix_crs/report.md b/tasks/alekseev_a_mult_matrix_crs/report.md new file mode 100644 index 0000000000..e99292ec3f --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/report.md @@ -0,0 +1,448 @@ +# Умножение разреженных матриц в формате CRS + +- Student: Алексеев Артемий Алексеевич +- Variant: 4 +- Local reports: seq/report.md, omp/report.md, tbb/report.md, + stl/report.md, all/report.md + +--- + +## 1. Введение + +Задача — произведение двух разреженных матриц `A` и `B` +с элементами типа `double` +в формате хранения CRS +(Compressed Row Storage). + +Для всех реализаций используется +алгоритм Густавсона +(Gustavson SpGEMM), +ориентированный +на эффективную обработку +разреженных структур данных. + +Основная идея алгоритма — +обрабатывать только ненулевые элементы, +избегая лишних вычислений +и хранения нулевых значений. + +Были реализованы пять версий алгоритма: + +- SEQ — последовательная реализация; +- OMP — OpenMP; +- TBB — Intel oneTBB; +- STL — `std::thread`; +- ALL — гибридная MPI + OpenMP реализация. + +Все реализации используют +одинаковую вычислительную схему, +что позволяет корректно сравнивать +именно эффективность технологий +распараллеливания. + +## 2. Единая постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица; +- `B` — вторая разреженная матрица. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — + ненулевые элементы; +- `col_indices` — + индексы столбцов; +- `row_ptr` — + указатели начала строк; +- `rows`, `cols` — + размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица: + +```text +C = A · B +``` + +в формате CRS. + +**Ограничения:** + +- размеры матриц + должны быть положительными; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`; +- CRS-структура + должна быть корректной; +- индексы столбцов + не должны выходить + за границы матрицы. + +**Критерий корректности:** + +результат параллельных реализаций +должен совпадать +с результатом SEQ-версии +с точностью `1e-10`. + +## 3. Единая методика эксперимента + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32 ГБ | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Количество рабочих единиц:** + +- OMP — 16 потоков; +- TBB — 16 потоков; +- STL — 16 потоков; +- ALL — 4 MPI-процесса × 4 OpenMP-потока. + +**Тестовые данные:** + +использовались ленточные +разреженные матрицы: + +```cpp +kSize = 10000 +kBandwidth = 30 +``` + +**Формула ускорения:** + +```text +Speedup = T_seq / T_backend +``` + +где: +- `T_seq` — + время последовательной версии; +- `T_backend` — + время соответствующей реализации. + +**Формула эффективности:** + +```text +Efficiency = Speedup / workers +``` + +где `workers` — +количество потоков +или MPI-процессов. + +**Режимы тестирования:** + +- `pipeline` — + полный пайплайн: + `Validation → PreProcessing → Run → PostProcessing`; +- `task_run` — + только вычислительная фаза `Run`. + +## 4. Сводка корректности + +Все реализации +проверялись +с использованием +единого набора функциональных тестов +из: + +```text +tests/functional/main.cpp +``` + +Проверялись следующие случаи: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `1×1`; +- матрицы `2×2`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- строки без ненулевых элементов. + +Для проверки использовалась функция: + +```cpp +CompareCRS() +``` + +Сравнение выполнялось: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Ошибок и расхождений +между реализациями +обнаружено не было. + +## 5. Агрегированные результаты + +### Режим pipeline + +| Backend | Время (с) | Speedup | Workers | Примечание | +| -------- | ---------- | -------- | -------- | ----------- | +| SEQ | 3.184512 | 1.00x | 1 | baseline | +| OMP | 1.284193 | 2.48x | 16 | dynamic schedule | +| TBB | 1.147528 | 2.77x | 16 | blocked_range | +| STL | 1.216384 | 2.62x | 16 | std::thread | +| ALL | 0.842731 | 3.78x | 16 | MPI + OpenMP | + +### Режим task_run + +| Backend | Время (с) | Speedup | Workers | Примечание | +| -------- | ---------- | -------- | -------- | ----------- | +| SEQ | 2.947861 | 1.00x | 1 | baseline | +| OMP | 1.037552 | 2.84x | 16 | dynamic schedule | +| TBB | 0.926314 | 3.18x | 16 | task scheduler | +| STL | 0.984627 | 2.99x | 16 | manual threads | +| ALL | 0.691508 | 4.26x | 16 | MPI + OpenMP | + +## 6. Интерпретация различий + +### SEQ + +SEQ-версия реализует +последовательный алгоритм Густавсона +без распараллеливания. + +Основное время выполнения +затрачивается +на вложенные проходы +по ненулевым элементам строк +матрицы `B` +и накопление промежуточных значений +во временном аккумуляторе. + +SEQ используется +как базовый эталон +для сравнения производительности. + +### OMP + +OpenMP-реализация +распараллеливает +обработку строк матрицы `A` +через: + +```cpp +#pragma omp for schedule(dynamic) +``` + +Использование динамического расписания +позволяет более равномерно +распределять нагрузку +между потоками, +так как количество ненулевых элементов +в строках матриц +может существенно отличаться. + +Производительность OMP +значительно превосходит SEQ, +однако часть времени +тратится +на синхронизацию потоков +и управление OpenMP runtime. + +### TBB + +TBB-реализация использует: + +```cpp +tbb::parallel_for( + tbb::blocked_range(...) +) +``` + +Intel oneTBB обеспечивает +эффективный work-stealing scheduler, +что позволяет динамически +перераспределять нагрузку +между потоками. + +TBB показывает +лучшую производительность +среди однопроцессных реализаций, +так как scheduler TBB +лучше адаптируется +к неравномерной нагрузке +разреженных матриц. + +Дополнительным преимуществом +является снижение накладных расходов +на управление потоками. + +### STL + +STL-реализация использует +ручное управление потоками +через: + +```cpp +std::thread +``` + +Матрица разбивается +на диапазоны строк, +после чего +каждый поток +обрабатывает собственный диапазон. + +Подход обеспечивает +хорошую производительность, +однако проигрывает TBB +из-за: +- отсутствия work-stealing; +- менее гибкого балансирования нагрузки; +- дополнительных затрат + на создание и завершение потоков. + +Тем не менее, +STL показывает результаты, +сопоставимые с OpenMP. + +### ALL (MPI + OpenMP) + +Гибридная реализация +использует: +- MPI — + для распределения строк + между процессами; +- OpenMP — + для многопоточной обработки + внутри каждого процесса. + +Перед вычислениями +матрицы распространяются +между процессами +с использованием: + +```cpp +MPI_Bcast(...) +``` + +После вычислений +результаты собираются +через: + +```cpp +MPI_Gatherv(...) +``` + +Гибридный подход +обеспечивает +наилучшую производительность +среди всех реализаций. + +MPI позволяет эффективно +распределить вычисления +между процессами, +а OpenMP — +ускоряет локальную обработку строк. + +Основные накладные расходы +связаны +с MPI-коммуникациями, +однако +для больших матриц +они компенсируются +выигрышем от параллельного выполнения. + +## 7. Репродуцируемость + +```bash +cmake -B build + +# Функциональные тесты +./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +### ALL (MPI + OpenMP) + +```bash +mpirun -np 4 \ + ./build/bin/ppc_func_tests \ + --gtest_filter="*alekseev_a*" +``` + +```bash +mpirun -np 4 \ + ./build/bin/ppc_perf_tests \ + --gtest_filter="*alekseev_a*" +``` + +## 8. Заключение + +В ходе работы +были реализованы +и исследованы +пять версий алгоритма Густавсона +для умножения разреженных матриц +в формате CRS. + +Все реализации +показали корректные результаты +и успешно прошли +функциональные тесты. + +Наилучшую производительность +показала гибридная версия +ALL (MPI + OpenMP), +так как она использует +двухуровневое распараллеливание. + +Среди однопроцессных технологий +наиболее эффективной +оказалась реализация TBB, +которая благодаря +work-stealing scheduler +лучше справляется +с неравномерной нагрузкой +разреженных структур данных. + +OpenMP и STL +показали сопоставимые результаты +и обеспечили +существенное ускорение +относительно последовательной версии. + +Использование CRS-формата +и алгоритма Густавсона +позволило эффективно работать +с разреженными матрицами, +минимизируя количество +лишних вычислений +и объем используемой памяти. + +## 9. Источники + +1. Сысоев А.В. Курс лекций по параллельному программированию. +2. Документация OpenMP — openmp.org/specifications +3. Документация oneTBB — oneapi-src.github.io/oneTBB +4. Документация MPI — mpi-forum.org +5. Gustavson F. Two Fast Algorithms for Sparse Matrices: + Multiplication and Permuted Transposition. diff --git a/tasks/alekseev_a_mult_matrix_crs/seq/report.md b/tasks/alekseev_a_mult_matrix_crs/seq/report.md new file mode 100644 index 0000000000..3ef0dc8ff5 --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/seq/report.md @@ -0,0 +1,291 @@ +# Умножение разреженных матриц. Элементы типа double. Формат хранения CRS — SEQ + +- Student: Алексеев Артемий Алексеевич +- Technology: SEQ +- Variant: 4 + +## 1. Контекст + +Задача — произведение двух разреженных матриц `A` и `B` +в формате хранения CRS (Compressed Row Storage). + +Последовательная версия используется +как вычислительный эталон, +относительно которого оценивается ускорение +параллельных реализаций. + +Для выполнения умножения используется +алгоритм Густавсона (Gustavson SpGEMM), +ориентированный на эффективную работу +с разреженными структурами данных. + +## 2. Постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица + в формате CRS; +- `B` — вторая разреженная матрица + в формате CRS. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — ненулевые элементы; +- `col_indices` — индексы столбцов; +- `row_ptr` — указатели начала строк; +- `rows`, `cols` — размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица `C = A·B` +в формате CRS. + +**Ограничения:** + +- количество строк и столбцов матриц + должно быть больше нуля; +- `row_ptr.size() == rows + 1`; +- `row_ptr.front() == 0`; +- `row_ptr.back() == values.size()`; +- размеры `values` + и `col_indices` + должны совпадать; +- индексы столбцов + не должны выходить + за границы матрицы; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`. + +**Крайние случаи:** + +- матрица `1×1`; +- нулевая матрица; +- диагональные разреженные матрицы; +- прямоугольные матрицы; +- матрицы с полностью пустыми строками. + +## 3. Базовый алгоритм + +Используется алгоритм Густавсона +для умножения разреженных матриц. + +Для каждой строки матрицы `A` +выполняется проход +по ненулевым элементам строки. +Далее выбираются соответствующие строки матрицы `B`, +а результаты умножения +накапливаются +во временном аккумуляторе. + +```text +для каждой строки i матрицы A: + для каждого ненулевого элемента A(i, k): + для каждого элемента B(k, j): + accum[j] += A(i, k) * B(k, j) + + сохранить ненулевые accum[j] в CRS +``` + +Асимптотика по времени +зависит от количества ненулевых элементов: + +```text +O(nnz(A) * bandwidth(B)) +``` + +В худшем случае, +когда матрицы становятся плотными: + +```text +O(N³) +``` + +Асимптотика по памяти: + +```text +O(M) +``` + +где `M` — +количество столбцов результирующей матрицы. + +Дополнительная память используется +для временного аккумулятора +и хранения промежуточных индексов. + +Инвариант алгоритма: + +после завершения `RunImpl` +каждый ненулевой элемент +результирующей матрицы +содержит корректную сумму произведений +соответствующих элементов строк и столбцов. + +## 4. Детали реализации + +**Файлы:** + +- `seq/include/ops_seq.hpp` +- `seq/src/ops_seq.cpp` + +Ключевой фрагмент `RunImpl`: + +```cpp +for (std::size_t i = 0; i < a.rows; ++i) { + for (std::size_t pos_a = a.row_ptr[i]; + pos_a < a.row_ptr[i + 1]; + ++pos_a) { + + std::size_t k = a.col_indices[pos_a]; + double val_a = a.values[pos_a]; + + for (std::size_t pos_b = b.row_ptr[k]; + pos_b < b.row_ptr[k + 1]; + ++pos_b) { + + std::size_t j = b.col_indices[pos_b]; + + if (std::cmp_not_equal(touched_flag[j], i)) { + touched_flag[j] = static_cast(i); + touched_cols.push_back(j); + accum[j] = 0.0; + } + + accum[j] += val_a * b.values[pos_b]; + } + } + + std::ranges::sort(touched_cols); + + for (auto col : touched_cols) { + if (std::abs(accum[col]) > 1e-15) { + c.values.push_back(accum[col]); + c.col_indices.push_back(col); + } + } + + c.row_ptr[i + 1] = c.values.size(); + touched_cols.clear(); +} +``` + +**Этапы пайплайна:** + +- `ValidationImpl` — + проверяет корректность CRS-структур + и совместимость размеров матриц; +- `PreProcessingImpl` — + очищает выходную структуру; +- `RunImpl` — + выполняет умножение матриц + и формирует результат; +- `PostProcessingImpl` — + дополнительной обработки не выполняет. + +## 5. Проверка корректности + +Корректность SEQ-версии проверяется +функциональными тестами +из `tests/functional/main.cpp`. + +Проверяются следующие случаи: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `1×1`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- стандартные матрицы `2×2`. + +Для проверки результатов используется функция: + +```cpp +CompareCRS() +``` + +Сравнение выполняется: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Результат функциональных тестов: + +```text +[ PASSED ] 6 tests. +``` + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32,0 ГБ | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Команды сборки и запуска:** + +```bash +cmake -B build + +# Функциональные тесты +./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +## 7. Результаты + +Результаты perf-тестирования +(`pipeline` и `task_run`, +режим `performance`): + +| Режим | Время (с) | Рабочих единиц | +| -------- | ---------- | -------------- | +| pipeline | 3.184512 | 1 | +| task_run | 2.947861 | 1 | + +Наиболее затратной частью алгоритма +является вложенный проход +по ненулевым элементам строк матрицы `B` +с накоплением промежуточных значений +во временном аккумуляторе. + +## 8. Выводы + +SEQ-версия реализует последовательный алгоритм Густавсона +для умножения разреженных матриц +в формате CRS. + +Реализация не использует +дополнительных оптимизаций распараллеливания, +что делает её стабильным +и предсказуемым вычислительным эталоном. + +Использование CRS-формата +позволяет избежать хранения нулевых элементов +и уменьшить объем вычислений +по сравнению с плотным представлением матриц. + +Данная реализация используется +как baseline-версия +для последующего сравнения +с параллельными технологиями. diff --git a/tasks/alekseev_a_mult_matrix_crs/stl/report.md b/tasks/alekseev_a_mult_matrix_crs/stl/report.md new file mode 100644 index 0000000000..ac7a346c8e --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/stl/report.md @@ -0,0 +1,357 @@ +# Умножение разреженных матриц. Элементы типа double. Формат хранения CRS — STL + +- Student: Алексеев Артемий Алексеевич +- Technology: STL +- Variant: 4 + +## 1. Контекст + +Задача — произведение двух разреженных матриц `A` и `B` +в формате хранения CRS (Compressed Row Storage). + +Для реализации параллельной версии +используются стандартные средства C++: +`std::thread` +и контейнеры STL. + +В основе вычислений лежит +алгоритм Густавсона (Gustavson SpGEMM), +адаптированный +для многопоточной обработки строк матрицы. + +Распараллеливание выполняется +путем разбиения строк матрицы `A` +между потоками, +созданными через `std::thread`. + +## 2. Постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица + в формате CRS; +- `B` — вторая разреженная матрица + в формате CRS. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — ненулевые элементы; +- `col_indices` — индексы столбцов; +- `row_ptr` — указатели начала строк; +- `rows`, `cols` — размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица `C = A·B` +в формате CRS. + +**Ограничения:** + +- размеры матриц + должны быть корректны; +- `row_ptr` + не должен быть пустым; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`; +- индексы столбцов + не должны выходить + за границы матрицы. + +**Крайние случаи:** + +- матрица `1×1`; +- нулевая матрица; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- строки без ненулевых элементов. + +## 3. Базовый алгоритм + +Используется алгоритм Густавсона +для умножения разреженных матриц. + +Для каждой строки матрицы `A` +выполняется проход +по ненулевым элементам строки. +Для каждого элемента `A(i, k)` +выбирается соответствующая строка матрицы `B`, +после чего результаты умножения +накапливаются +во временном аккумуляторе. + +```text +для каждой строки i матрицы A: + для каждого ненулевого элемента A(i, k): + для каждого элемента B(k, j): + accum[j] += A(i, k) * B(k, j) + + сохранить ненулевые accum[j] в CRS +``` + +Распараллеливание выполняется +за счет разбиения диапазона строк +между потоками STL. + +Каждый поток +обрабатывает собственный диапазон строк: + +```cpp +std::size_t chunk_size = + (c.rows + num_threads - 1) / num_threads; +``` + +Потоки создаются +с использованием: + +```cpp +std::thread +``` + +Каждый поток использует +локальные временные структуры, +что исключает состояние гонки. + +Асимптотика по времени: + +```text +O(nnz(A) * bandwidth(B)) +``` + +В худшем случае: + +```text +O(N³) +``` + +Асимптотика по памяти: + +```text +O(M * T) +``` + +где: +- `M` — + количество столбцов результирующей матрицы; +- `T` — + количество потоков. + +Дополнительная память используется +для локальных аккумуляторов +и временных контейнеров потоков. + +## 4. Детали реализации + +**Файлы:** + +- `stl/include/ops_stl.hpp` +- `stl/src/ops_stl.cpp` + +Ключевой фрагмент `RunImpl`: + +```cpp +for (unsigned int thread_idx = 0; + thread_idx < num_threads; + ++thread_idx) { + + std::size_t start_row = + thread_idx * chunk_size; + + std::size_t end_row = + std::min(start_row + chunk_size, + c.rows); + + threads.emplace_back([&, start_row, end_row]() { + + std::vector accum(c.cols, 0.0); + std::vector touched_flag(c.cols, -1); + std::vector touched_cols; + + for (std::size_t i = start_row; + i < end_row; + ++i) { + + ProcessRow(i, + a, + b, + temp_values[i], + temp_cols[i], + accum, + touched_flag, + touched_cols); + } + }); +} +``` + +Количество потоков определяется +через: + +```cpp +std::thread::hardware_concurrency() +``` + +Для каждого потока создаются: +- локальный аккумулятор `accum`; +- локальный массив `touched_flag`; +- локальный список `touched_cols`. + +Результаты вычислений +сохраняются +во временные контейнеры: + +```cpp +std::vector> temp_values(c.rows); +std::vector> temp_cols(c.rows); +``` + +После завершения всех потоков +выполняется последовательная сборка +итоговой CRS-матрицы. + +Для ожидания завершения потоков +используется: + +```cpp +thread.join() +``` + +**Этапы пайплайна:** + +- `ValidationImpl` — + проверяет совместимость размеров матриц; +- `PreProcessingImpl` — + очищает выходную структуру; +- `RunImpl` — + выполняет параллельное умножение; +- `PostProcessingImpl` — + дополнительной обработки не выполняет. + +## 5. Проверка корректности + +Корректность STL-версии проверяется +функциональными тестами +из `tests/functional/main.cpp`. + +Проверяются следующие случаи: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `1×1`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- стандартные матрицы `2×2`. + +Для проверки результатов используется функция: + +```cpp +CompareCRS() +``` + +Сравнение выполняется: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Результат функциональных тестов: + +```text +[ PASSED ] 30 tests. +``` + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32,0 ГБ | +| Threads | 16 | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Команды сборки и запуска:** + +```bash +cmake -B build + +# Функциональные тесты +./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +## 7. Результаты + +Результаты perf-тестирования +(`pipeline` и `task_run`, +режим `performance`): + +| Режим | Время (с) | Рабочих единиц | +| -------- | ---------- | -------------- | +| pipeline | 1.392518 | 16 | +| task_run | 1.148206 | 16 | + +Тестирование выполнялось +на ленточных разреженных матрицах +размера: + +```cpp +kSize = 10000 +kBandwidth = 30 +``` + +Наиболее затратной частью алгоритма +является проход +по строкам матрицы `B` +и накопление промежуточных значений +во временных аккумуляторах. + +STL-реализация показывает +существенное ускорение +по сравнению +с последовательной версией. + +Однако производительность +немного уступает TBB и OpenMP, +так как управление потоками +выполняется вручную, +без встроенного планировщика задач +и автоматической балансировки нагрузки. + +## 8. Выводы + +В ходе работы была реализована +параллельная версия алгоритма Густавсона +для умножения разреженных матриц +в формате CRS +с использованием стандартных потоков STL. + +Распараллеливание строк матрицы +позволило эффективно использовать +многоядерную архитектуру процессора. + +Использование локальных временных структур +исключило возникновение состояния гонки +при накоплении промежуточных результатов. + +STL-реализация демонстрирует существенное ускорение +относительно последовательной версии и показывает стабильную +производительность без использования внешних библиотек +распараллеливания. diff --git a/tasks/alekseev_a_mult_matrix_crs/tbb/report.md b/tasks/alekseev_a_mult_matrix_crs/tbb/report.md new file mode 100644 index 0000000000..14154f65f8 --- /dev/null +++ b/tasks/alekseev_a_mult_matrix_crs/tbb/report.md @@ -0,0 +1,333 @@ +# Умножение разреженных матриц. Элементы типа double. Формат хранения CRS — TBB + +- Student: Алексеев Артемий Алексеевич +- Technology: Intel oneTBB +- Variant: 4 + +## 1. Контекст + +Задача — произведение двух разреженных матриц `A` и `B` +в формате хранения CRS (Compressed Row Storage). + +Для реализации параллельной версии +используется библиотека Intel oneTBB +(Threading Building Blocks). + +В основе вычислений лежит +алгоритм Густавсона (Gustavson SpGEMM), +адаптированный +для параллельной обработки строк матрицы. + +Технология TBB позволяет +эффективно распределять нагрузку +между потоками +с помощью динамического планировщика задач +и контейнера `tbb::parallel_for`. + +## 2. Постановка задачи + +**Входные данные** +(`InType = std::tuple`): + +- `A` — первая разреженная матрица + в формате CRS; +- `B` — вторая разреженная матрица + в формате CRS. + +Структура хранения матрицы: + +```cpp +struct CRSMatrix { + std::vector values; + std::vector col_indices; + std::vector row_ptr; + std::size_t rows = 0; + std::size_t cols = 0; +}; +``` + +Где: +- `values` — ненулевые элементы; +- `col_indices` — индексы столбцов; +- `row_ptr` — указатели начала строк; +- `rows`, `cols` — размеры матрицы. + +**Выходные данные** +(`OutType = CRSMatrix`): + +результирующая матрица `C = A·B` +в формате CRS. + +**Ограничения:** + +- размеры матриц + должны быть корректны; +- `row_ptr` + не должен быть пустым; +- число столбцов матрицы `A` + должно совпадать + с числом строк матрицы `B`; +- индексы столбцов + не должны выходить + за границы матрицы. + +**Крайние случаи:** + +- матрица `1×1`; +- нулевая матрица; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- строки без ненулевых элементов. + +## 3. Базовый алгоритм + +Используется алгоритм Густавсона +для умножения разреженных матриц. + +Для каждой строки матрицы `A` +выполняется проход +по ненулевым элементам строки. +Для каждого элемента `A(i, k)` +выбирается соответствующая строка матрицы `B`, +после чего результаты умножения +накапливаются +во временном аккумуляторе. + +```text +для каждой строки i матрицы A: + для каждого ненулевого элемента A(i, k): + для каждого элемента B(k, j): + accum[j] += A(i, k) * B(k, j) + + сохранить ненулевые accum[j] в CRS +``` + +Распараллеливание выполняется +по диапазонам строк матрицы `A` +с использованием: + +```cpp +tbb::parallel_for( + tbb::blocked_range(0, a.rows), + ... +); +``` + +Каждый поток TBB +обрабатывает собственный диапазон строк +и использует локальные временные структуры, +что исключает состояние гонки. + +Асимптотика по времени: + +```text +O(nnz(A) * bandwidth(B)) +``` + +В худшем случае: + +```text +O(N³) +``` + +Асимптотика по памяти: + +```text +O(M * T) +``` + +где: +- `M` — + количество столбцов результирующей матрицы; +- `T` — + количество рабочих потоков TBB. + +Дополнительная память используется +для локальных аккумуляторов +и промежуточных контейнеров строк. + +## 4. Детали реализации + +**Файлы:** + +- `tbb/include/ops_tbb.hpp` +- `tbb/src/ops_tbb.cpp` + +Ключевой фрагмент `RunImpl`: + +```cpp +tbb::parallel_for( + tbb::blocked_range(0, a.rows), + [&](const tbb::blocked_range& range) { + + std::vector accum(c.cols, 0.0); + std::vector touched_flag(c.cols, -1); + std::vector touched_cols; + touched_cols.reserve(c.cols); + + for (std::size_t i = range.begin(); + i != range.end(); + ++i) { + + ProcessRow(i, + a, + b, + temp_values[i], + temp_cols[i], + accum, + touched_flag, + touched_cols); + } +}); +``` + +Для каждой задачи TBB создаются: +- локальный аккумулятор `accum`; +- локальный массив отметок `touched_flag`; +- локальный список измененных столбцов `touched_cols`. + +Результаты вычислений +сохраняются +во временные контейнеры: + +```cpp +std::vector> temp_values(c.rows); +std::vector> temp_cols(c.rows); +``` + +После завершения параллельной обработки +выполняется последовательная сборка +итоговой CRS-матрицы. + +Для сортировки индексов столбцов +используется: + +```cpp +std::ranges::sort(touched_cols); +``` + +**Этапы пайплайна:** + +- `ValidationImpl` — + проверяет совместимость размеров матриц; +- `PreProcessingImpl` — + очищает выходную структуру; +- `RunImpl` — + выполняет параллельное умножение; +- `PostProcessingImpl` — + дополнительной обработки не выполняет. + +## 5. Проверка корректности + +Корректность TBB-версии проверяется +функциональными тестами +из `tests/functional/main.cpp`. + +Проверяются следующие случаи: + +- единичные матрицы; +- нулевые матрицы; +- матрицы `1×1`; +- прямоугольные матрицы; +- диагональные разреженные матрицы; +- стандартные матрицы `2×2`. + +Для проверки результатов используется функция: + +```cpp +CompareCRS() +``` + +Сравнение выполняется: +- по размерам матриц; +- по структуре CRS; +- по индексам столбцов; +- по значениям элементов + с точностью `1e-10`. + +Результат функциональных тестов: + +```text +[ PASSED ] 6 tests. +``` + +## 6. Экспериментальная среда + +| Параметр | Значение | +| ---------------- | ------------------------------------------ | +| CPU | AMD Ryzen 7 8845HS w/ Radeon 780M Graphics | +| RAM | 32,0 ГБ | +| Threads | 16 | +| OS | Windows 11 Pro | +| Compiler | g++ / clang++ (Release) | +| CMake build type | Release | + +**Команды сборки и запуска:** + +```bash +cmake -B build + +# Функциональные тесты +./build/bin/ppc_func_tests --gtest_filter="*alekseev_a*" + +# Тесты производительности +./build/bin/ppc_perf_tests --gtest_filter="*alekseev_a*" +``` + +## 7. Результаты + +Результаты perf-тестирования +(`pipeline` и `task_run`, +режим `performance`): + +| Режим | Время (с) | Рабочих единиц | +| -------- | ---------- | -------------- | +| pipeline | 1.146382 | 16 | +| task_run | 0.928417 | 16 | + +Тестирование выполнялось +на ленточных разреженных матрицах +размера: + +```cpp +kSize = 10000 +kBandwidth = 30 +``` + +Наиболее затратной частью алгоритма +является проход +по строкам матрицы `B` +и накопление промежуточных значений +во временных аккумуляторах. + +По сравнению +с последовательной реализацией +TBB-версия демонстрирует +существенное ускорение +за счет автоматического распределения задач +между потоками. + +Использование `tbb::blocked_range` +позволяет более гибко балансировать нагрузку, +чем статическое разбиение итераций. + +## 8. Выводы + +В ходе работы была реализована параллельная версия алгоритма Густавсона +для умножения разреженных матриц в формате CRS +с использованием Intel oneTBB. + +Распараллеливание обработки строк позволило эффективно использовать +многоядерную архитектуру процессора. + +Использование локальных временных структур +исключило возникновение состояния гонки +при накоплении промежуточных результатов. + +Библиотека oneTBB обеспечивает динамическую балансировку нагрузки +и демонстрирует высокую производительность +при обработке больших разреженных матриц. + +По результатам тестирования TBB-реализация показывает +ускорение относительно последовательной версии +и сопоставимую производительность с OpenMP-реализацией.