Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
783653c
feat: sequential convolution
RodionovMaxim05 Mar 19, 2025
f0da9f9
feat: new filters and better output
RodionovMaxim05 Mar 20, 2025
0c7779c
tests: add unit tests
RodionovMaxim05 Mar 20, 2025
a141781
refactor: code improvement
RodionovMaxim05 Mar 27, 2025
d634e5c
test: refactor tests
RodionovMaxim05 Mar 27, 2025
bda79d8
ci: add linter and formatter
RodionovMaxim05 Mar 27, 2025
bbfc842
feat: add split image into parts
RodionovMaxim05 Mar 28, 2025
8e9839f
chore: improve usage, add scripts
RodionovMaxim05 Apr 3, 2025
df781d5
test: add benchmark
RodionovMaxim05 Apr 4, 2025
f38680a
test: add perf benchmark
RodionovMaxim05 Apr 4, 2025
6add049
test: add benchmark for another image
RodionovMaxim05 Apr 5, 2025
f332d24
docs: update README
RodionovMaxim05 Apr 5, 2025
aeed502
refactor: move 'stb_image' to root of project, change 'get_time_in_se…
RodionovMaxim05 Apr 9, 2025
2138a1b
refactor: 'main.c' and 'test_main.c'
RodionovMaxim05 Apr 9, 2025
564b126
docs: add documentation
RodionovMaxim05 Apr 9, 2025
1abfccf
feat: add py requirements.txt
RodionovMaxim05 Apr 9, 2025
dbc7264
docs: update documentation
RodionovMaxim05 Apr 10, 2025
64a9fa4
tests: update benchmarks results
RodionovMaxim05 Apr 11, 2025
77527c4
docs: add analysis of benchmark results
RodionovMaxim05 Apr 11, 2025
6c20cb4
docs: update analysis
RodionovMaxim05 Apr 14, 2025
d344d1b
docs: improve Readme, add debug and release build
RodionovMaxim05 Apr 17, 2025
7a65aeb
tests: add composition tests
RodionovMaxim05 Apr 17, 2025
f40c05a
tests: add benchmark for filter composition
RodionovMaxim05 Apr 18, 2025
390831e
tests: update benchmark results
RodionovMaxim05 Apr 18, 2025
8195fdb
docs: add analysis of sequential application of filters and their com…
RodionovMaxim05 Apr 18, 2025
1fc4308
chore: minor corrections
RodionovMaxim05 Apr 18, 2025
8f64849
refactor: test directory, use of size_t for sizes
RodionovMaxim05 May 5, 2025
4adb16e
fix: running tests
RodionovMaxim05 May 20, 2025
a34bb56
feat: queue-based pipeline processing
RodionovMaxim05 May 20, 2025
9cb4dae
docs: add docs for pipeline processing functions
RodionovMaxim05 May 20, 2025
833d767
fix: queue, improve message output
RodionovMaxim05 May 23, 2025
9eda8c3
tests: add queue benchmark
RodionovMaxim05 May 23, 2025
a3245d6
tests: add queue benchmarks results
RodionovMaxim05 May 23, 2025
b594a57
docs: add analysis of queue mode
RodionovMaxim05 May 24, 2025
ae3226f
Merge branch 'feedback' into main
RodionovMaxim05 May 24, 2025
cb2f876
chore: revert merge commit changes
RodionovMaxim05 May 24, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ jobs:
run: ./scripts/run_clang_tidy.sh

- name: Test
run: ./scripts/test.sh
run: ./scripts/test.sh
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ dkms.conf
__pycache__
build
pics/
*_cat.bmp
*_monkey.bmp
images/*_cat.bmp
images/*_monkey.bmp
output_queue_mode/
92 changes: 92 additions & 0 deletions Analysis_of_queue_benchmark_results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Анализ производительности режима очередей (`queue mode`)

## Введение

В рамках данного анализа исследуется эффективность реализации параллельной обработки изображений в архитектуре очередей (`--mode=queue`). В этой модели обработка изображений разбита между тремя типами потоков, которые соединяются очередьми с ограничениями по размеру добавляемого изображения, образуя тем самым конвейер обработки:
- `readers` - отвечают за чтение исходных данных;
- `workers` - выполняют применение фильтра `bl`;
- `writers` - записывают результаты обработки.

**Конвейер:**
```
[Reader(s)] → [Input Queue] → [Worker(s)] → [Output Queue] → [Writer(s)]
```
`Reader` читает изображение и помещает его во входную очередь (`Input Queue`) → `Worker` извлекает изображения из `Input Queue`, выполняет свертку, помещает результат в выходную очередь (`Output Queue`) → `Writer` извлекает результат из `Output Queue` и записывает в файл.

Тестирование проводилось на наборе из [9 изображений](input_queue_mode/), полученных применением различных фильтров к [`cat.bmp`](images/cat.bmp). Таким образом, все изображения имеют одинаковый размер - `5.9 MiB`, что позволяет минимизировать влияние объёма данных на результаты тестирования. Однако их содержимое различно, поэтому каждое изображение необходимо отдельно загружать в память и записывать результат обратно, в отличие от случая с полностью идентичными изображениями, когда можно было бы загрузить файл один раз и использовать его повторно.

Также стоит отметить, что расположение мьютексов при работе с условными переменными в конце функциях [`queue_push`](src/queue_mode/queue.c#L111) и [`queue_pop`](src/queue_mode/queue.c#L145) выбрано не случайно. Именно такая организация блокировок позволяет достичь минимального времени выполнения программы. Ключевым моментом является то, что в функции `queue_pop` [`pop_mutex`](src/queue_mode/queue.c#L149) освобождается только после освобождения [`push_mutex`](src/queue_mode/queue.c#L147) при отправке сигнала о появлении свободного места в очереди. Это гарантирует, что поток `worker` сможет раньше начать претендовать на доступ к новым данным из очереди. Если бы `pop_mutex` освобождался раньше, до попытки захвата [`queue_push`](src/queue_mode/queue.c#L145), `worker` оказывался бы в состоянии ожидания освобождения `push_mutex` (которое чаще всего долгое).

Эксперимент охватывал следующие параметры:
1) Конфигурации распределения потоков между ролями: (`readers`, `workers`, `writers`);
2) Лимиты памяти :
- `6 MiB` - одновременно в очереди может находиться только одно изображение;
- `30 MiB` - до пяти изображений могут быть в очереди;
- `55 MiB` - очередь позволяет хранить все изображения.
3) Число потоков для применения фильтра `bl`: `--thread=2`, `--thread=3`, `--thread=4`, `--thread=5`.

**Количество прогонов для каждого теста:** 40 (обеспечивает статистическую достоверность результатов).

**Код бенчмарка**: [`tests/benchmarks/comparison_of_queue_mode.py`](tests/benchmarks/comparison_of_queue_mode.py)

## Конфигурация системы:

- **Процессор:** Процессор: Intel Core i7-11370H (4 физических ядра, 8 логических потоков) с фиксированной частотой 3.3 GHz (`sudo cpupower frequency-set --min 3300MHz --max 3300MHz`);
- **Кэш L1:** 80 КБ на ядро (48 КБ – кэш данных, 32 КБ – кэш инструкций);
- **Кэш L2:** 1.25 МБ на ядро;
- **Общий кэш L3:** 12 МБ.

- **ОЗУ:** 16ГБ

- **ОС:** Linux Kubuntu 24.04

- **Настройки производительности:** Использовался режим `performance` (`sudo cpupower frequency-set -g performance`) для минимизации влияния динамического изменения частоты процессора.

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

## Результаты тестирования

В ходе тестирования измерялось:
- Общее время выполнения программы;
- Время работы каждого этапа - чтение, обработка и запись изображения.

Графики и соответствующие им результаты находятся в папках [`tests/plots/queue_mode_<N>`](tests/plots/), где N обозначает число потоков для применения фильтра.

## Анализ результатов

### Влияние количества потоков (`--thread`) и `mem_lim`
- **`--thread=2`**:
- Наилучшая конфигурация: **1 Reader, 3 Workers, 2 Writers** (`mem_lim=6`, Среднее время - **1.78 сек.**).
- Увеличение `mem_limit` до 55 MiB ухудшает время.
- Увеличение общего числа создаваемых потоков (`readers` + `workers` + `writers` + `threads`) сверх 8 не приводит к улучшению производительности, поскольку процессор поддерживает максимум 8 логических потоков. При превышении этого числа возникает конкуренция за вычислительные ресурсы, так как планировщик операционной системы вынужден выполнять частые переключения контекста между потоками. Это увеличивает накладные расходы и ухудшает производительность вместо её повышения. Поэтому в дальнейших тестах суммарное количество потоков намеренно ограничивалось значением 8.

- **`--thread=3`**:
- Оптимальная производительность: **1 Reader, 3 Workers, 1 Writer** (`mem_lim=6`, Среднее время - **1.735 сек.**).
- При увеличении `mem_lim` время также увеличивается.

- **`--thread=4`**:
- Лучший результат: **1 Reader, 2 Workers, 1 Writer** (`mem_lim=6`, Среднее время - **1.726 сек.**).
- При увеличении `mem_lim` время также увеличивается.

- **`--thread=5`**:
- Минимальное среднее время: **1.74 сек.** (конфигурация `1-1-1`, `mem_lim=6`).

**Объяснение:**

**1.** Результаты показывают, что обработка изображения является узким местом всего pipeline'а. По этой причине увеличение числа потоков, ответственных за применение фильтра, приводит к снижению времени выполнения этого этапа, что в свою очередь положительно влияет на общее время выполнения программы.

**2.** Во всех рассмотренных случаях наилучшие результаты достигаются при минимальном лимите памяти (**6 MiB**). Увеличение лимита до **30** и **55 MiB** не улучшает, а в большинстве случаев даже его ухудшает. Это объясняется особенностями работы синхронизации между потоками `readers` и `workers` в контексте входной очереди (`Input queue`). При малом лимите памяти (**6 MiB**), очередь позволяет хранить только одно изображение, поэтому читатель вынужден ожидать освобождения места (пока `worker` заберёт это изображение). Это создаёт небольшую задержку для `reader'а`, но позволяет `worker` немедленно приступить к обработке, что особенно важно, поскольку этап обработки является самым медленным звеном pipeline. Напротив, при увеличении лимита памяти, `reader` может загрузить сразу несколько изображений в очередь без ожидания, что повышает вероятность конкуренции за мьютексы (`push_mutex` и `pop_mutex`) между `readers` и `workers`.
В результате:
- Рабочие тратят больше времени на ожидание блокировок;
- Начало обработки первого изображения откладывается;
- Общее время выполнения программы возрастает.

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

## Вывод

Минимальное время выполнения программы зафиксировано при конфигурации `--thread=4`, `1 Reader`, `2 Workers`, `1 Writer`, `mem_lim=6` - среднее время: **1.726 сек.**

Этот результат демонстрирует, что для достижения максимальной производительности необходимо:
- Ограничивать размер очереди, чтобы он соответствовал размеру одного изображения.
- Уменьшить время обработки изображений - найти баланс между ускорением выполнения сверстки (`--thread=4`) и количеством одновременно работающих `worker'ов`.
6 changes: 1 addition & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")

add_subdirectory(src)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules")

find_package(CMocka CONFIG)
if (CMocka_FOUND)
include(AddCMockaTest)
include(AddMockedTest)
add_subdirectory(tests)
enable_testing()
add_subdirectory(tests)
endif(CMocka_FOUND)
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,29 @@

## Description

Image-convolution is an image processing application that applies various convolution filters to images. It supports both sequential and parallel execution modes with different workload distribution strategies. The application is designed for benchmarking and comparing different parallelization approaches for image processing algorithms.
Image-convolution is an image processing tool that applies convolution filters in sequential, parallel (with different workload distribution strategies), and queue-based pipeline modes. In queue mode, readers, workers, and writers operate concurrently in a producer-consumer model with memory-limited queues. The app is designed for benchmarking different parallelization strategies and thread scalability.

## Usage
Basic Command:
```bash
./build/src/image-convolution <image_path> <filter_name> --mode=<mode> [--thread=<num>]
```
### Options
| Parameter | Description | Required |
|--------------------|---------------------------------------------------------------------|----------|
| `<image_path>` | Path to input image or `--default-image` (predefined default image) | Yes |
| `<filter_name>` | Filter to apply (see [Available Filters](#available-filters)) | Yes |
| `--mode=<mode>` | Execution mode: `seq`, `pixel`, `row`, `column`, `block` | Yes |
| `--thread=<num>` | Number of threads (for parallel modes) | No* |
| Parameter | Description |
|--------------------|-----------------------------------------------------------------------------|
| `<image_path>` | Path to input image or `--default-image` (predefined default image) |
| `<filter_name>` | Filter to apply (see [Available Filters](#available-filters)) |
| `--mode=<mode>` | Execution mode: `seq`, `pixel`, `row`, `column`, `block` or `queue` |
| `--thread=<num>` | Number of threads to use for parallel convolution (ignored if `--mode=seq`) |

*Required for all modes except seq
#### Queue options
| Parameter | Description |
|--------------------|---------------------------------------------------------------------|
| `--num=<images>` | Number of images to process |
| `--readers=<num>` | Number of reader threads |
| `--workers=<num>` | Number of worker threads |
| `--writers=<num>` | Number of writer threads |
| `--mem_lim=<MiB>` | Memory limit for queues in MiB (e.g. 10) |

### Available Filters
| Name | Description | Kernel Size |
Expand All @@ -47,6 +54,10 @@ Basic Command:
```bash
./build/src/image-convolution images/cat.bmp gbl --mode=block --thread=4
```
3) Queue-Based pipeline processing:
```bash
./build/src/image-convolution images mbl --mode=queue --thread=2 --num=25 --readers=2 --workers=3 --writers=2 --mem_lim=15
```

## Build
To build the project:
Expand All @@ -68,6 +79,10 @@ The project includes three types of tests:
```bash
./scripts/perf.sh <image_name>
```
4) Queue mode performance analysis - evaluate execution time distribution across stages (reader, worker, writer) for various thread configurations under a memory limit:
```bash
./scripts/queue_benchmark.sh <num_of_imgs> <mem_lim>
```

## Prerequisites for Benchmarks (`Performance benchmarks` and `Cache performance analysis`)
Before running benchmarks, you need to set up a Python virtual environment and install dependencies:
Expand Down
Loading