Skip to content
Open
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
30 changes: 30 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Install OpenCV
run: sudo apt-get update && sudo apt-get install -y libjpeg-dev libpng-dev libtiff-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libopencv-dev
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ bld/
# .NET Core
project.lock.json
project.fragment.lock.json
BenchmarkDotNet.Artifacts

# ASP.NET Scaffolding
ScaffoldingReadMe.txt
Expand Down
13 changes: 13 additions & 0 deletions Convolver.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E7170ACE-9AA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BMPConvolver.Core", "src\BMPConvolver.Core\BMPConvolver.Core.csproj", "{523C0934-7353-45C5-AB89-DFB54C98071F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BMPConvolver.Cli", "src\BMPConvolver.Cli\BMPConvolver.Cli.csproj", "{A99E9047-15E4-492A-8C77-B2C82CAF0B0B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5F4B7EAD-0AB8-4FBF-85F1-EA0BEE09832F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BMPConvolver.Tests", "test\BMPConvolver.Tests\BMPConvolver.Tests.csproj", "{69A51A1D-CE2E-4C75-AD2E-64ADC5F8E593}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bench", "bench", "{2507B97A-9F2C-4A9C-A7A3-58541DDFA976}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BMPConvolver.Benchmarks", "bench\BMPConvolver.Benchmarks\BMPConvolver.Benchmarks.csproj", "{4E6BFCD2-D954-4FB9-AC30-1024FA7A9A3C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -35,5 +45,8 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{523C0934-7353-45C5-AB89-DFB54C98071F} = {E7170ACE-9AAD-4689-ADC6-22E048D93B80}
{A99E9047-15E4-492A-8C77-B2C82CAF0B0B} = {E7170ACE-9AAD-4689-ADC6-22E048D93B80}
{69A51A1D-CE2E-4C75-AD2E-64ADC5F8E593} = {5F4B7EAD-0AB8-4FBF-85F1-EA0BEE09832F}
{4E6BFCD2-D954-4FB9-AC30-1024FA7A9A3C} = {2507B97A-9F2C-4A9C-A7A3-58541DDFA976}
EndGlobalSection
EndGlobal
171 changes: 171 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,172 @@
# Запуск

Для запуска приложения выполните следующую команду:

```bash
dotnet run --project src/BMPConvolver.Cli/BMPConvolver.Cli.csproj <input.bmp> <output.bmp> [опции]
```

Опции включают:
- `--mode seq|par`: режим выполнения (последовательный или параллельный)
- `--partition pixels|rows|cols|grid`: способ разделения работы
- `--grid <block width>x<block height>`: размер сетки для grid partition
- `--border zero|clamp`: режим обработки границ
- `--kernel box3|sharpen|identity`: предустановленный kernel
- `--kernel-text "..."`: kernel в текстовом формате
- `--kernel-file path.txt`: kernel из файла

## Тесты

Для запуска тестов:

```bash
dotnet test
```

## Бенчмарки

Для запуска бенчмарков:

```bash
dotnet run --project bench/BMPConvolver.Benchmarks/BMPConvolver.Benchmarks.csproj
```

# Анализ производительнсти
- **Зависимость от ширины и высоты**: При фиксированной высоте увеличение ширины приводит к росту времени в 4-5 раз, тогда как при фиксированной ширине увеличение высоты дает рост в 2-3 раза. Такая разнца связана с особенностями кэширования памяти и последовательным доступом к данным в массивах.
- **Зависимость от способа параллелизма**:
- **Sequential**: Базовый последовательный метод, без параллелизма. Примерно в 5 раз медленнее самого эффективного параллельного метода
- **Parallel.Pixels**: Параллелизм по каждому пикселю создает огромное количество задач, что приводит к высоким накладным расходам на синхронизацию. Самый медленный метод с высокой дисперсией.
- **Parallel.Rows**: Параллелизм по строкам эффективен для изображений с большим количеством строк. Разделяет изображение на горизонтальные полосы, минимизируя конфликты памяти. Часто быстрее Parallel.Columns.
- **Parallel.Columns**: Параллелизм по столбцам показывает себя лучше на изображениях с большим количеством столбцов. Однако он может страдать от неравномерности нагрузки при широких изображениях.
- **Parallel.Grid**: Наиболее эффективный метод, разделяющий изображение на прямоугольные блоки (сетку). Балансирует нагрузку между потоками, минимизирует накладные расходы и обеспечивает лучшую масштабируемость. Часто в 2-3 раза быстрее Sequential и в 10+ раз быстрее Parallel.Pixels.

## Border mode: Zero
### Диаграмма
![Столбчатая_диаграмма_Zero](public/img/Border_mode_Zero_columnar.png)

### График
![График_Zero](public/img/Border_mode_Zero_Graphics.png)

### Таблица
| Method | Width | Height | Mean | Error |
|----------------- |------ |------- |-----------:|-----------:|
| Sequential | 1024 | 1024 | 11.347 ms | 0.1680 ms |
| Parallel.Pixels | 1024 | 1024 | 24.677 ms | 1.3195 ms |
| Parallel.Rows | 1024 | 1024 | 2.510 ms | 0.0188 ms |
| Parallel.Columns | 1024 | 1024 | 2.313 ms | 0.0259 ms |
| Parallel.Grid | 1024 | 1024 | 2.247 ms | 0.0162 ms |
| | | | | |
| Sequential | 1024 | 2048 | 22.219 ms | 0.0945 ms |
| Parallel.Pixels | 1024 | 2048 | 51.281 ms | 5.7572 ms |
| Parallel.Rows | 1024 | 2048 | 4.961 ms | 0.0466 ms |
| Parallel.Columns | 1024 | 2048 | 5.252 ms | 0.0444 ms |
| Parallel.Grid | 1024 | 2048 | 4.517 ms | 0.0601 ms |
| | | | | |
| Sequential | 1024 | 4096 | 48.451 ms | 0.1076 ms |
| Parallel.Pixels | 1024 | 4096 | 110.977 ms | 19.1888 ms |
| Parallel.Rows | 1024 | 4096 | 9.724 ms | 0.0335 ms |
| Parallel.Columns | 1024 | 4096 | 10.589 ms | 0.0641 ms |
| Parallel.Grid | 1024 | 4096 | 8.937 ms | 0.0491 ms |
| | | | | |
| Sequential | 2048 | 1024 | 24.379 ms | 0.1265 ms |
| Parallel.Pixels | 2048 | 1024 | 49.390 ms | 6.5355 ms |
| Parallel.Rows | 2048 | 1024 | 4.982 ms | 0.0440 ms |
| Parallel.Columns | 2048 | 1024 | 6.388 ms | 0.1713 ms |
| Parallel.Grid | 2048 | 1024 | 5.264 ms | 0.1405 ms |
| | | | | |
| Sequential | 2048 | 2048 | 48.880 ms | 0.2759 ms |
| Parallel.Pixels | 2048 | 2048 | 102.091 ms | 8.1502 ms |
| Parallel.Rows | 2048 | 2048 | 9.650 ms | 0.0547 ms |
| Parallel.Columns | 2048 | 2048 | 9.829 ms | 0.1386 ms |
| Parallel.Grid | 2048 | 2048 | 9.884 ms | 0.0655 ms |
| | | | | |
| Sequential | 2048 | 4096 | 98.503 ms | 0.8038 ms |
| Parallel.Pixels | 2048 | 4096 | 236.901 ms | 4.6947 ms |
| Parallel.Rows | 2048 | 4096 | 18.314 ms | 0.2121 ms |
| Parallel.Columns | 2048 | 4096 | 20.790 ms | 0.1510 ms |
| Parallel.Grid | 2048 | 4096 | 19.169 ms | 0.1776 ms |
| | | | | |
| Sequential | 4096 | 1024 | 48.863 ms | 0.2360 ms |
| Parallel.Pixels | 4096 | 1024 | 103.113 ms | 11.9344 ms |
| Parallel.Rows | 4096 | 1024 | 8.819 ms | 0.0642 ms |
| Parallel.Columns | 4096 | 1024 | 9.795 ms | 0.0708 ms |
| Parallel.Grid | 4096 | 1024 | 9.115 ms | 0.2993 ms |
| | | | | |
| Sequential | 4096 | 2048 | 96.191 ms | 3.7277 ms |
| Parallel.Pixels | 4096 | 2048 | 223.957 ms | 24.3798 ms |
| Parallel.Rows | 4096 | 2048 | 16.587 ms | 0.0625 ms |
| Parallel.Columns | 4096 | 2048 | 22.102 ms | 0.0916 ms |
| Parallel.Grid | 4096 | 2048 | 20.067 ms | 0.2590 ms |
| | | | | |
| Sequential | 4096 | 4096 | 203.417 ms | 3.6494 ms |
| Parallel.Pixels | 4096 | 4096 | 483.104 ms | 3.5202 ms |
| Parallel.Rows | 4096 | 4096 | 37.907 ms | 0.3498 ms |
| Parallel.Columns | 4096 | 4096 | 45.954 ms | 0.4596 ms |
| Parallel.Grid | 4096 | 4096 | 38.499 ms | 0.2745 ms |


## Border mode: Clamp

### Диаграмма
![Столбчатая_диаграмма_Clamp](public/img/Border_mode_Clamp_columnar.png)

### График
![График_Clamp](public/img/Border_mode_Clamp_Graphics.png)

### Таблица

| Method | Width | Height | Mean | Error |
|----------------- |------ |------- |-----------:|-----------:|
| Sequential | 1024 | 1024 | 10.899 ms | 0.0522 ms |
| Parallel.Pixels | 1024 | 1024 | 25.133 ms | 1.8560 ms |
| Parallel.Rows | 1024 | 1024 | 2.423 ms | 0.0177 ms |
| Parallel.Columns | 1024 | 1024 | 2.210 ms | 0.0153 ms |
| Parallel.Grid | 1024 | 1024 | 2.339 ms | 0.0334 ms |
| | | | | |
| Sequential | 1024 | 2048 | 23.711 ms | 0.1069 ms |
| Parallel.Pixels | 1024 | 2048 | 49.684 ms | 5.6650 ms |
| Parallel.Rows | 1024 | 2048 | 4.385 ms | 0.0375 ms |
| Parallel.Columns | 1024 | 2048 | 4.687 ms | 0.0334 ms |
| Parallel.Grid | 1024 | 2048 | 4.933 ms | 0.0552 ms |
| | | | | |
| Sequential | 1024 | 4096 | 43.248 ms | 0.2635 ms |
| Parallel.Pixels | 1024 | 4096 | 102.942 ms | 13.7667 ms |
| Parallel.Rows | 1024 | 4096 | 9.432 ms | 0.0324 ms |
| Parallel.Columns | 1024 | 4096 | 10.499 ms | 0.1717 ms |
| Parallel.Grid | 1024 | 4096 | 9.198 ms | 0.0589 ms |
| | | | | |
| Sequential | 2048 | 1024 | 22.027 ms | 0.1667 ms |
| Parallel.Pixels | 2048 | 1024 | 47.339 ms | 5.4978 ms |
| Parallel.Rows | 2048 | 1024 | 4.699 ms | 0.1312 ms |
| Parallel.Columns | 2048 | 1024 | 6.117 ms | 0.1464 ms |
| Parallel.Grid | 2048 | 1024 | 5.095 ms | 0.0555 ms |
| | | | | |
| Sequential | 2048 | 2048 | 47.986 ms | 0.2603 ms |
| Parallel.Pixels | 2048 | 2048 | 97.947 ms | 9.1101 ms |
| Parallel.Rows | 2048 | 2048 | 8.399 ms | 0.1387 ms |
| Parallel.Columns | 2048 | 2048 | 9.672 ms | 0.1301 ms |
| Parallel.Grid | 2048 | 2048 | 9.903 ms | 0.2243 ms |
| | | | | |
| Sequential | 2048 | 4096 | 90.954 ms | 6.1812 ms |
| Parallel.Pixels | 2048 | 4096 | 233.673 ms | 26.1762 ms |
| Parallel.Rows | 2048 | 4096 | 20.059 ms | 0.3383 ms |
| Parallel.Columns | 2048 | 4096 | 21.500 ms | 0.2026 ms |
| Parallel.Grid | 2048 | 4096 | 20.564 ms | 0.3094 ms |
| | | | | |
| Sequential | 4096 | 1024 | 43.379 ms | 0.2542 ms |
| Parallel.Pixels | 4096 | 1024 | 111.441 ms | 20.5576 ms |
| Parallel.Rows | 4096 | 1024 | 8.383 ms | 0.0320 ms |
| Parallel.Columns | 4096 | 1024 | 9.556 ms | 0.0487 ms |
| Parallel.Grid | 4096 | 1024 | 10.015 ms | 0.0773 ms |
| | | | | |
| Sequential | 4096 | 2048 | 98.364 ms | 1.2503 ms |
| Parallel.Pixels | 4096 | 2048 | 213.736 ms | 31.6862 ms |
| Parallel.Rows | 4096 | 2048 | 17.360 ms | 0.0749 ms |
| Parallel.Columns | 4096 | 2048 | 22.656 ms | 0.1534 ms |
| Parallel.Grid | 4096 | 2048 | 18.816 ms | 0.2907 ms |
| | | | | |
| Sequential | 4096 | 4096 | 190.988 ms | 16.7715 ms |
| Parallel.Pixels | 4096 | 4096 | 470.957 ms | 4.9517 ms |
| Parallel.Rows | 4096 | 4096 | 35.611 ms | 0.3819 ms |
| Parallel.Columns | 4096 | 4096 | 47.020 ms | 0.2583 ms |
| Parallel.Grid | 4096 | 4096 | 35.363 ms | 0.5478 ms |
18 changes: 18 additions & 0 deletions bench/BMPConvolver.Benchmarks/BMPConvolver.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\..\src\BMPConvolver.Core\BMPConvolver.Core.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
</ItemGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
87 changes: 87 additions & 0 deletions bench/BMPConvolver.Benchmarks/ConvolverBench.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BMPConvolver.Core;
using BMPConvolver.Core.WorkPartitioning;

namespace BMPConvolver.Benchmarks;

[Config(typeof(Config))]
[HideColumns(Column.RatioSD, Column.AllocRatio, Column.Gen0, Column.Gen1, Column.Gen2)]
public class ConvolutionBench
{
private sealed class Config : ManualConfig
{
public Config()
{
AddJob(Job.Default.WithWarmupCount(3).WithIterationCount(15));
}
}
private const int Seed = 1;

[Params(1024, 2048, 4096)]
public int Width { get; set; }

[Params(1024, 2048, 4096)]
public int Height { get; set; }

[Params(BorderMode.Zero, BorderMode.Clamp)]
public BorderMode Border { get; set; }

public int KernelSize { get; set; } = 3;

public int Grid { get; set; } = 8;

private GrayImage _input = null!;
private Kernel _kernel = null!;

[GlobalSetup]
public void Setup()
{
_input = RandomImage(Width, Height);
_kernel = RandomKernelOddFrom(KernelSize, KernelSize);
}

[Benchmark(Baseline = true, Description = "Sequential")]
public GrayImage Sequential()
=> Convolver.ConvolveSequential(_input, _kernel, Border);

[Benchmark(Description = "Parallel.Pixels")]
public GrayImage ParallelByPixels()
=> Convolver.ConvolveParallel(_input, _kernel, Border, PartitioningMode.Pixels, gridX: Grid, gridY: Grid);

[Benchmark(Description = "Parallel.Rows")]
public GrayImage ParallelByRows()
=> Convolver.ConvolveParallel(_input, _kernel, Border, PartitioningMode.Rows, gridX: Grid, gridY: Grid);

[Benchmark(Description = "Parallel.Columns")]
public GrayImage ParallelByColumns()
=> Convolver.ConvolveParallel(_input, _kernel, Border, PartitioningMode.Columns, gridX: Grid, gridY: Grid);

[Benchmark(Description = "Parallel.Grid")]
public GrayImage ParallelByGrid()
=> Convolver.ConvolveParallel(_input, _kernel, Border, PartitioningMode.Grid, gridX: Grid, gridY: Grid);

private static GrayImage RandomImage(int width, int height)
{
var rnd = new Random(Seed);
var pixels = new float[width * height];
for (var i = 0; i < pixels.Length; i++)
pixels[i] = (float)rnd.NextDouble();
return new GrayImage(width, height, pixels);
}

private static Kernel RandomKernelOddFrom(int widthRequested, int heightRequested)
{
var width = widthRequested <= 1 ? 1 : (widthRequested % 2 == 1 ? widthRequested : widthRequested + 1);
var height = heightRequested <= 1 ? 1 : (heightRequested % 2 == 1 ? heightRequested : heightRequested + 1);

var rnd = new Random(Seed);
var weights = new float[width * height];
for (var i = 0; i < weights.Length; i++)
weights[i] = ((float)rnd.NextDouble() - 0.5f) * 0.5f;
return new Kernel(width, height, width / 2, height / 2, weights);
}
}

12 changes: 12 additions & 0 deletions bench/BMPConvolver.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using BenchmarkDotNet.Running;

namespace BMPConvolver.Benchmarks;

public static class Program
{
public static int Main(string[] args)
{
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
return 0;
}
}
Binary file added public/img/Border_mode_Clamp_Graphics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/Border_mode_Clamp_columnar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/Border_mode_Zero_Graphics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/Border_mode_Zero_columnar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/BMPConvolver.Cli/BMPConvolver.Cli.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\BMPConvolver.Core\BMPConvolver.Core.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
</ItemGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading
Loading