Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "kamalagin_a_binary_image_convex_hull/common/include/common.hpp"
#include "task/include/task.hpp"

namespace kamalagin_a_binary_image_convex_hull {

class KamalaginABinaryImageConvexHullALL : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kALL;
}
explicit KamalaginABinaryImageConvexHullALL(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
};

} // namespace kamalagin_a_binary_image_convex_hull
122 changes: 122 additions & 0 deletions tasks/kamalagin_a_binary_image_convex_hull/all/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Построение выпуклой оболочки для компонент бинарного изображения — ALL

- Студент: Камалягин Артём Аркадьевич
- Технология: ALL
- Вариант: 30

## 1. Контекст

ALL-версия реализует гибридную схему MPI + OpenMP. MPI используется для распределения найденных компонент между
процессами, а OpenMP — для построения выпуклых оболочек внутри каждого процесса.

Важно: данный отчёт описывает версию ALL после фикса. Если фикс не смог пройти ci, то в основном репозитории до
обновления pull request могла оставаться старая thread-only реализация, где ALL совмещала OpenMP, `std::thread` и TBB
внутри одного процесса.

Цель этой версии — проверить, насколько выгодно совмещать межпроцессное распределение данных и внутрипроцессный
параллелизм на задаче построения оболочек для компонент бинарного изображения.

## 2. Постановка задачи

Постановка совпадает с root report и SEQ baseline: для каждой 4-связной foreground-компоненты бинарного изображения
требуется построить выпуклую оболочку.

Результат ALL сравнивается с теми же ожидаемыми ответами, что и остальные backend-и.

## 3. Базовый алгоритм

Реализация сосредоточена в `SolveAllMpi`:

1. Проверить входное изображение.
2. На `rank = 0` последовательно найти все компоненты (`ExtractComponents`).
3. Разделить компоненты между MPI-процессами и передать их через `MPI_Scatter` / `MPI_Scatterv`.
4. На каждом rank построить локальные оболочки (`BuildLocalHulls`, OpenMP).
5. Собрать оболочки на `rank = 0` через `MPI_Gatherv`.
6. Разослать итоговый `HullList` всем rank через `MPI_Bcast`.

## 4. Межпроцессная схема

MPI используется для распределения списка компонент между процессами. Процесс с `rank = 0` выполняет поиск компонент и
подготавливает данные для рассылки. Затем компоненты делятся между MPI-процессами.

Сначала `MPI_Scatter` передаёт размер локального буфера, затем `MPI_Scatterv` — сами компоненты, так как разным
процессам может достаться разное число точек. После локального построения оболочек результаты собираются на `rank = 0`
через `MPI_Gatherv`.

После сборки итоговый результат рассылается всем процессам через `MPI_Bcast`. Это нужно, чтобы каждый процесс имел
согласованный `HullList` после завершения вычисления.

## 5. Внутрипроцессная схема

Внутри каждого MPI-процесса локальный набор компонент обрабатывается с помощью OpenMP. Для компонент строятся выпуклые
оболочки независимо, поэтому цикл по локальным компонентам можно распараллелить.

Каждый OpenMP-поток записывает результат только для своей компоненты. За счёт этого гонки записи отсутствуют. Общий
порядок результата восстанавливается после сборки данных на `rank = 0`.

## 6. Детали реализации

Файлы: `all/include/ops_all.hpp`, `all/src/ops_all.cpp`.

Потенциальные узкие места:

- `ExtractComponents` на `rank = 0` остаётся последовательным;
- MPI-передачи компонент и оболочек добавляют накладные расходы;
- при малой стоимости одной компоненты overhead может быть больше полезной работы.

Гонок записи нет: каждый OpenMP-поток пишет в свой элемент `hulls[i]`, а MPI-ранги работают с непересекающимися
диапазонами компонент.

## 7. Проверка корректности

ALL проверяется на тех же functional-тестах, что и SEQ, OMP, TBB и STL: пустой вход, одна точка, изолированные точки,
линии, прямоугольник и две компоненты.

Согласованность результата обеспечивается тем, что оболочки собираются по индексу компоненты на `rank = 0`, после чего
итог рассылается всем процессам. Перед сравнением список оболочек нормализуется.

## 8. Экспериментальная среда

- CPU: Intel Core i5-12400F
- OS: Windows
- compiler: MSVC
- build type: `Release`
- performance input: `5000 × 5000`
- `PPC_NUM_PROC`: `2` (число MPI-процессов)
- `PPC_NUM_THREADS`: `1`, `2`, `4`, `8` (OpenMP-потоки внутри процесса)

В таблице `Threads` — число OpenMP-потоков при фиксированных двух MPI-процессах.

Команды запуска:

```powershell
$env:PPC_NUM_THREADS='4'
$env:PPC_NUM_PROC='2'
mpiexec -n 2 -env PPC_NUM_THREADS 4 -env OMP_NUM_THREADS 4 `
.\build\bin\ppc_func_tests.exe --gtest_filter="*kamalagin_a_binary_image_convex_hull_all*"
mpiexec -n 2 -env PPC_NUM_THREADS 4 -env OMP_NUM_THREADS 4 `
.\build\bin\ppc_perf_tests.exe --gtest_filter="*task_run_kamalagin_a_binary_image_convex_hull_all_enabled*"
```

## 9. Результаты

| Threads | Time, s | Speedup | Efficiency |
| ---: | ---: | ---: | ---: |
| 1 | 0.0410 | 0.90 | 90.5% |
| 2 | 0.0403 | 0.92 | 46.0% |
| 4 | 0.0401 | 0.93 | 23.1% |
| 8 | 0.0398 | 0.93 | 11.7% |

ALL не показывает ускорения относительно SEQ. Это ожидаемо: поиск компонент последовательный на `rank = 0`, а
MPI-обмены на мелких компонентах добавляют заметные накладные расходы.

## 10. Выводы

ALL-версия корректно распределяет найденные компоненты между MPI-процессами и строит локальные оболочки с помощью
OpenMP.

На текущем performance-входе ускорение относительно SEQ не получено. Основная причина состоит в том, что поиск компонент
остаётся последовательным на `rank = 0`, а MPI-передачи добавляют накладные расходы.

При увеличении числа OpenMP-потоков время меняется слабо. Стоимость построения оболочек для малых компонент
недостаточна, чтобы полностью компенсировать расходы гибридной схемы.
Loading
Loading