From 8945f95c8aa3e7e9fc2b47b16ca294c1fd309319 Mon Sep 17 00:00:00 2001 From: Stefan Seeland <168659+stesee@users.noreply.github.com> Date: Mon, 4 May 2026 08:25:46 +0000 Subject: [PATCH 1/2] Add benchmarking for image comparison and update project files - Introduced a new benchmark project for image comparison using BenchmarkDotNet. - Added benchmark methods to evaluate image equality with various options. - Updated the solution file to include the new benchmarks project. - Modified build and artifact handling in the CI workflow for benchmarks. --- .github/workflows/dotnet.yml | 26 +++++++--- SkiaSharpCompare.Benchmarks/Program.cs | 47 +++++++++++++++++ .../SkiaSharpCompare.Benchmarks.csproj | 25 +++++++++ SkiaSharpCompare.sln | 52 ++++++++++++++++++- 4 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 SkiaSharpCompare.Benchmarks/Program.cs create mode 100644 SkiaSharpCompare.Benchmarks/SkiaSharpCompare.Benchmarks.csproj diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 55940ad..8b43f53 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -59,6 +59,18 @@ jobs: - name: Test run: dotnet test --no-build --verbosity normal --configuration Release + - name: Benchmark + if: runner.os == 'Linux' + run: | + dotnet run --configuration Release --project SkiaSharpCompare.Benchmarks/SkiaSharpCompare.Benchmarks.csproj -- --artifacts ./artifacts/benchmarks + + - name: Upload benchmark artifacts + if: runner.os == 'Linux' + uses: actions/upload-artifact@v7 + with: + name: benchmark-artifacts-${{ matrix.os }} + path: ./artifacts/benchmarks + deployRelease: if: startsWith(github.ref, 'refs/heads/release') runs-on: ubuntu-latest @@ -76,7 +88,7 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore - - name: Download CLI artifacts + - name: Download build artifacts uses: actions/download-artifact@v8 with: path: ./artifacts_download @@ -92,9 +104,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} if: env.GITHUB_TOKEN != '' run: | - # Attach all downloaded CLI artifacts regardless of OS - zip -r cli-artifacts.zip ./artifacts_download - gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg cli-artifacts.zip --generate-notes + # Attach downloaded build artifacts (CLI and benchmark outputs). + zip -r build-artifacts.zip ./artifacts_download + gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg build-artifacts.zip --generate-notes deployTest: if: ${{ !startsWith(github.ref, 'refs/heads/release') }} @@ -113,7 +125,7 @@ jobs: run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore - - name: Download CLI artifacts + - name: Download build artifacts uses: actions/download-artifact@v8 with: path: ./artifacts_download @@ -130,5 +142,5 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} if: env.GITHUB_TOKEN != '' run: | - zip -r cli-artifacts.zip ./artifacts_download - gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg cli-artifacts.zip --prerelease --generate-notes + zip -r build-artifacts.zip ./artifacts_download + gh release create ${{env.CURRENT_VERSION}} ./SkiaSharpCompare/bin/Release/*.*nupkg build-artifacts.zip --prerelease --generate-notes diff --git a/SkiaSharpCompare.Benchmarks/Program.cs b/SkiaSharpCompare.Benchmarks/Program.cs new file mode 100644 index 0000000..d375d2f --- /dev/null +++ b/SkiaSharpCompare.Benchmarks/Program.cs @@ -0,0 +1,47 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using Codeuctivity.SkiaSharpCompare; + +namespace SkiaSharpCompare.Benchmarks; + +public static class Program +{ + public static void Main(string[] args) + { + BenchmarkRunner.Run(); + } +} + +[MemoryDiagnoser] +public class ImagesAreEqualBenchmarks +{ + private string actualPath = string.Empty; + private string expectedPath = string.Empty; + private ImageCompare sut = null!; + + [Params(ResizeOption.Resize, ResizeOption.DontResize)] + public ResizeOption ResizeOption { get; set; } + + [Params(TransparencyOptions.CompareAlphaChannel, TransparencyOptions.IgnoreAlphaChannel)] + public TransparencyOptions TransparencyOptions { get; set; } + + [GlobalSetup] + public void Setup() + { + actualPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); + expectedPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); + + if (!File.Exists(actualPath) || !File.Exists(expectedPath)) + { + throw new FileNotFoundException($"Missing benchmark files: '{actualPath}' or '{expectedPath}'."); + } + + sut = new ImageCompare(ResizeOption, TransparencyOptions); + } + + [Benchmark] + public bool ImagesAreEqual_ByPath() + { + return sut.ImagesAreEqual(actualPath, expectedPath); + } +} diff --git a/SkiaSharpCompare.Benchmarks/SkiaSharpCompare.Benchmarks.csproj b/SkiaSharpCompare.Benchmarks/SkiaSharpCompare.Benchmarks.csproj new file mode 100644 index 0000000..0a9a137 --- /dev/null +++ b/SkiaSharpCompare.Benchmarks/SkiaSharpCompare.Benchmarks.csproj @@ -0,0 +1,25 @@ + + + + + + + + + + + + + TestData\%(RecursiveDir)%(Filename)%(Extension) + PreserveNewest + + + + + Exe + net10.0 + enable + enable + + + diff --git a/SkiaSharpCompare.sln b/SkiaSharpCompare.sln index 55a8a70..44fe809 100644 --- a/SkiaSharpCompare.sln +++ b/SkiaSharpCompare.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11222.15 d18.0 +VisualStudioVersion = 18.0.11222.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A1B6F6C3-ECBE-471C-AE18-199DEF5982C3}" ProjectSection(SolutionItems) = preProject @@ -21,28 +21,78 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpCompare.Cli", "Ski EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpCompare.Cli.Tests", "SkiaSharpCompare.Cli.Tests\SkiaSharpCompare.Cli.Tests.csproj", "{059AD3EF-A494-850F-6457-E5C4D706CC94}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharpCompare.Benchmarks", "SkiaSharpCompare.Benchmarks\SkiaSharpCompare.Benchmarks.csproj", "{72AFC588-4BF8-42D4-A4E3-23F096C72823}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|x64.Build.0 = Debug|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Debug|x86.Build.0 = Debug|Any CPU {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|Any CPU.ActiveCfg = Release|Any CPU {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|Any CPU.Build.0 = Release|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|x64.ActiveCfg = Release|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|x64.Build.0 = Release|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|x86.ActiveCfg = Release|Any CPU + {556DA894-7456-4DE2-9E02-3815FE159D7F}.Release|x86.Build.0 = Release|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|x64.ActiveCfg = Debug|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|x64.Build.0 = Debug|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|x86.ActiveCfg = Debug|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Debug|x86.Build.0 = Debug|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|Any CPU.Build.0 = Release|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|x64.ActiveCfg = Release|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|x64.Build.0 = Release|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|x86.ActiveCfg = Release|Any CPU + {9CFA9A6F-92FC-40E3-BB85-4C8F95EFC8C7}.Release|x86.Build.0 = Release|Any CPU {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|x64.ActiveCfg = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|x64.Build.0 = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|x86.ActiveCfg = Debug|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Debug|x86.Build.0 = Debug|Any CPU {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|Any CPU.ActiveCfg = Release|Any CPU {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|Any CPU.Build.0 = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|x64.ActiveCfg = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|x64.Build.0 = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|x86.ActiveCfg = Release|Any CPU + {296B4D6C-C8F6-81DE-9B65-D4CE48783B52}.Release|x86.Build.0 = Release|Any CPU {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|x64.ActiveCfg = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|x64.Build.0 = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|x86.ActiveCfg = Debug|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Debug|x86.Build.0 = Debug|Any CPU {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|Any CPU.ActiveCfg = Release|Any CPU {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|Any CPU.Build.0 = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|x64.ActiveCfg = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|x64.Build.0 = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|x86.ActiveCfg = Release|Any CPU + {059AD3EF-A494-850F-6457-E5C4D706CC94}.Release|x86.Build.0 = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|x64.ActiveCfg = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|x64.Build.0 = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|x86.ActiveCfg = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Debug|x86.Build.0 = Debug|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|Any CPU.Build.0 = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|x64.ActiveCfg = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|x64.Build.0 = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|x86.ActiveCfg = Release|Any CPU + {72AFC588-4BF8-42D4-A4E3-23F096C72823}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From fd8e9ea1b29fddb3bf60c3f72e2eda6828f3b9ea Mon Sep 17 00:00:00 2001 From: Stefan Seeland <168659+stesee@users.noreply.github.com> Date: Mon, 4 May 2026 08:33:12 +0000 Subject: [PATCH 2/2] Format code for consistency in Program.cs --- SkiaSharpCompare.Benchmarks/Program.cs | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/SkiaSharpCompare.Benchmarks/Program.cs b/SkiaSharpCompare.Benchmarks/Program.cs index d375d2f..163f15f 100644 --- a/SkiaSharpCompare.Benchmarks/Program.cs +++ b/SkiaSharpCompare.Benchmarks/Program.cs @@ -6,42 +6,42 @@ namespace SkiaSharpCompare.Benchmarks; public static class Program { - public static void Main(string[] args) - { - BenchmarkRunner.Run(); - } + public static void Main(string[] args) + { + BenchmarkRunner.Run(); + } } [MemoryDiagnoser] public class ImagesAreEqualBenchmarks { - private string actualPath = string.Empty; - private string expectedPath = string.Empty; - private ImageCompare sut = null!; - - [Params(ResizeOption.Resize, ResizeOption.DontResize)] - public ResizeOption ResizeOption { get; set; } - - [Params(TransparencyOptions.CompareAlphaChannel, TransparencyOptions.IgnoreAlphaChannel)] - public TransparencyOptions TransparencyOptions { get; set; } - - [GlobalSetup] - public void Setup() - { - actualPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); - expectedPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); - - if (!File.Exists(actualPath) || !File.Exists(expectedPath)) - { - throw new FileNotFoundException($"Missing benchmark files: '{actualPath}' or '{expectedPath}'."); - } - - sut = new ImageCompare(ResizeOption, TransparencyOptions); - } - - [Benchmark] - public bool ImagesAreEqual_ByPath() - { - return sut.ImagesAreEqual(actualPath, expectedPath); - } + private string actualPath = string.Empty; + private string expectedPath = string.Empty; + private ImageCompare sut = null!; + + [Params(ResizeOption.Resize, ResizeOption.DontResize)] + public ResizeOption ResizeOption { get; set; } + + [Params(TransparencyOptions.CompareAlphaChannel, TransparencyOptions.IgnoreAlphaChannel)] + public TransparencyOptions TransparencyOptions { get; set; } + + [GlobalSetup] + public void Setup() + { + actualPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); + expectedPath = Path.Combine(AppContext.BaseDirectory, "TestData", "Calc0.jpg"); + + if (!File.Exists(actualPath) || !File.Exists(expectedPath)) + { + throw new FileNotFoundException($"Missing benchmark files: '{actualPath}' or '{expectedPath}'."); + } + + sut = new ImageCompare(ResizeOption, TransparencyOptions); + } + + [Benchmark] + public bool ImagesAreEqual_ByPath() + { + return sut.ImagesAreEqual(actualPath, expectedPath); + } }