diff --git a/.gitignore b/.gitignore
index add57be..8b3bbe2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ bin/
obj/
/packages/
riderModule.iml
-/_ReSharper.Caches/
\ No newline at end of file
+/_ReSharper.Caches/
+*Artifacts/
diff --git a/src/Oh.My.Stitcher.Benchmark/Oh.My.Stitcher.Benchmark.csproj b/src/Oh.My.Stitcher.Benchmark/Oh.My.Stitcher.Benchmark.csproj
new file mode 100644
index 0000000..be0402a
--- /dev/null
+++ b/src/Oh.My.Stitcher.Benchmark/Oh.My.Stitcher.Benchmark.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Oh.My.Stitcher.Benchmark/OperationBenchmark.cs b/src/Oh.My.Stitcher.Benchmark/OperationBenchmark.cs
new file mode 100644
index 0000000..80cfff6
--- /dev/null
+++ b/src/Oh.My.Stitcher.Benchmark/OperationBenchmark.cs
@@ -0,0 +1,45 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using NetVips;
+using Oh.My.Stitcher.NetVips;
+
+namespace Oh.My.Stitcher.Benchmark;
+
+[SimpleJob(RuntimeMoniker.Net80)]
+[MemoryDiagnoser]
+public class OperationBenchmark
+{
+ // CHANGE ME
+ private const string TILES_DIRECTORY = "/home/formulatrix/Downloads/tiles1705";
+
+ [Benchmark(Baseline = true)] // Mark this as the baseline for comparison
+ public void Standard_Operation()
+ {
+ const string rect = "A1:AE55";
+ if( !Tile.TryParseRect(rect, out int minRow, out int maxRow, out int minCol, out int maxCol) )
+ throw new ArgumentException($"Invalid canvas_rect: '{rect}'");
+
+ for( int row = minRow; row <= maxRow; row++ )
+ for( int col = minCol; col <= maxCol; col++ )
+ {
+ string path = Tile.FullPath(TILES_DIRECTORY, row, col);
+ using var i = ( Operation.Call("pngload", path) as Image )!;
+ }
+ }
+
+ [Benchmark]
+ public void Fast_Operation()
+ {
+ const string rect = "A1:AE55";
+ if( !Tile.TryParseRect(rect, out int minRow, out int maxRow, out int minCol, out int maxCol) )
+ throw new ArgumentException($"Invalid canvas_rect: '{rect}'");
+
+ Span pathBuffer = stackalloc byte[512];
+ for( int row = minRow; row <= maxRow; row++ )
+ for( int col = minCol; col <= maxCol; col++ )
+ {
+ int length = Tile.FullPathFast(TILES_DIRECTORY, row, col, pathBuffer);
+ using var i = ( OperationHacks.Call("pngload", pathBuffer.Slice(0, length + 1)) as Image )!;
+ }
+ }
+}
diff --git a/src/Oh.My.Stitcher.Benchmark/PathCreationBenchmark.cs b/src/Oh.My.Stitcher.Benchmark/PathCreationBenchmark.cs
new file mode 100644
index 0000000..08be29f
--- /dev/null
+++ b/src/Oh.My.Stitcher.Benchmark/PathCreationBenchmark.cs
@@ -0,0 +1,41 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+
+namespace Oh.My.Stitcher.Benchmark;
+
+[SimpleJob(RuntimeMoniker.Net80)]
+[MemoryDiagnoser]
+public class PathCreationBenchmark
+{
+ // CHANGE ME
+ private const string TILES_DIRECTORY = "/home/formulatrix/Downloads/tiles1705";
+
+ [Benchmark(Baseline = true)] // Mark this as the baseline for comparison
+ public void Standard_PathCombine()
+ {
+ const string rect = "A1:AE55";
+ if( !Tile.TryParseRect(rect, out int minRow, out int maxRow, out int minCol, out int maxCol) )
+ throw new ArgumentException($"Invalid canvas_rect: '{rect}'");
+
+ for( int row = minRow; row <= maxRow; row++ )
+ for( int col = minCol; col <= maxCol; col++ )
+ {
+ Tile.FullPath(TILES_DIRECTORY, row, col);
+ }
+ }
+
+ [Benchmark]
+ public void Fast_PathCombine()
+ {
+ const string rect = "A1:AE55";
+ if( !Tile.TryParseRect(rect, out int minRow, out int maxRow, out int minCol, out int maxCol) )
+ throw new ArgumentException($"Invalid canvas_rect: '{rect}'");
+
+ Span pathBuffer = stackalloc byte[512];
+ for( int row = minRow; row <= maxRow; row++ )
+ for( int col = minCol; col <= maxCol; col++ )
+ {
+ Tile.FullPathFast(TILES_DIRECTORY, row, col, pathBuffer);
+ }
+ }
+}
diff --git a/src/Oh.My.Stitcher.Benchmark/Program.cs b/src/Oh.My.Stitcher.Benchmark/Program.cs
new file mode 100644
index 0000000..a8af081
--- /dev/null
+++ b/src/Oh.My.Stitcher.Benchmark/Program.cs
@@ -0,0 +1,13 @@
+using BenchmarkDotNet.Running;
+
+namespace Oh.My.Stitcher.Benchmark;
+
+static class Program
+{
+ static void Main(string[] _)
+ {
+ BenchmarkRunner.Run();
+ BenchmarkRunner.Run();
+ BenchmarkRunner.Run();
+ }
+}
diff --git a/src/Oh.My.Stitcher.Benchmark/UsageBenchmark.cs b/src/Oh.My.Stitcher.Benchmark/UsageBenchmark.cs
new file mode 100644
index 0000000..9ae34f8
--- /dev/null
+++ b/src/Oh.My.Stitcher.Benchmark/UsageBenchmark.cs
@@ -0,0 +1,62 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Jobs;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Logging;
+using NetVips;
+using NSubstitute;
+
+namespace Oh.My.Stitcher.Benchmark;
+
+[SimpleJob(RuntimeMoniker.Net80)]
+[MemoryDiagnoser]
+public class UsageBenchmark
+{
+ // CHANGE ME
+ private const string TILES_DIRECTORY = "/home/formulatrix/Downloads/tiles1705";
+
+ [Benchmark(Baseline = true)] // Mark this as the baseline for comparison
+ public void Standard_Usage()
+ {
+ Stitch request = new()
+ {
+ CanvasRect = "A1:AE55", CropOffset = new CropOffset(0, 0), CropSize = new CropSize(1, 1), OutputScale = 1
+ };
+ Image? image = null;
+ List images = [];
+ IMemoryCache cache = Substitute.For();
+ ILogger logger = Substitute.For();
+ try
+ {
+ Tile.TryCreate(in request, TILES_DIRECTORY, images, logger, cache, out image, out string? _, out string? _);
+ }
+ finally
+ {
+ image?.Dispose();
+ foreach( Image img in images )
+ img.Dispose();
+ }
+ }
+
+ [Benchmark]
+ public void Fast_Usage()
+ {
+ Stitch request = new()
+ {
+ CanvasRect = "A1:AE55", CropOffset = new CropOffset(0, 0), CropSize = new CropSize(1, 1), OutputScale = 1
+ };
+ Image? image = null;
+ List images = [];
+ IMemoryCache cache = Substitute.For();
+ ILogger logger = Substitute.For();
+ try
+ {
+ Tile.TryCreateFast(in request, TILES_DIRECTORY, images, logger, cache, out image, out string? _, out string? _);
+ }
+ finally
+ {
+ image?.Dispose();
+ foreach( Image img in images )
+ img.Dispose();
+ }
+ }
+}
diff --git a/stitchaton.sln b/stitchaton.sln
index 6f825ef..a2f972a 100644
--- a/stitchaton.sln
+++ b/stitchaton.sln
@@ -4,6 +4,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AEE9B1D3-6AD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oh.My.Stitcher", "src\Oh.My.Stitcher\Oh.My.Stitcher.csproj", "{9AB5F809-0D6A-4906-AB89-DC797FB7CF42}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oh.My.Stitcher.Benchmark", "src\Oh.My.Stitcher.Benchmark\Oh.My.Stitcher.Benchmark.csproj", "{68A9786C-87C0-488C-9DA4-29909671F4F0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "vendor", "vendor", "{2F99FC95-31EA-42FD-BA0B-948B7DB33D86}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetVips", "vendor\NetVips\NetVips.csproj", "{A27A7D25-4601-45A5-ACA0-7DE44AE7FD33}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -12,12 +17,21 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9AB5F809-0D6A-4906-AB89-DC797FB7CF42} = {AEE9B1D3-6AD8-4EEE-800B-2873B0BB78DD}
- {A9CC8F78-CB38-4986-9480-5FB4556F1356} = {AEE9B1D3-6AD8-4EEE-800B-2873B0BB78DD}
+ {68A9786C-87C0-488C-9DA4-29909671F4F0} = {AEE9B1D3-6AD8-4EEE-800B-2873B0BB78DD}
+ {A27A7D25-4601-45A5-ACA0-7DE44AE7FD33} = {2F99FC95-31EA-42FD-BA0B-948B7DB33D86}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9AB5F809-0D6A-4906-AB89-DC797FB7CF42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9AB5F809-0D6A-4906-AB89-DC797FB7CF42}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9AB5F809-0D6A-4906-AB89-DC797FB7CF42}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9AB5F809-0D6A-4906-AB89-DC797FB7CF42}.Release|Any CPU.Build.0 = Release|Any CPU
+ {68A9786C-87C0-488C-9DA4-29909671F4F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {68A9786C-87C0-488C-9DA4-29909671F4F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {68A9786C-87C0-488C-9DA4-29909671F4F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {68A9786C-87C0-488C-9DA4-29909671F4F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A27A7D25-4601-45A5-ACA0-7DE44AE7FD33}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {A27A7D25-4601-45A5-ACA0-7DE44AE7FD33}.Debug|Any CPU.Build.0 = Debug|x64
+ {A27A7D25-4601-45A5-ACA0-7DE44AE7FD33}.Release|Any CPU.ActiveCfg = Release|x64
+ {A27A7D25-4601-45A5-ACA0-7DE44AE7FD33}.Release|Any CPU.Build.0 = Release|x64
EndGlobalSection
EndGlobal
diff --git a/stitchaton.sln.DotSettings b/stitchaton.sln.DotSettings
index d094dec..8f3153e 100644
--- a/stitchaton.sln.DotSettings
+++ b/stitchaton.sln.DotSettings
@@ -1,2 +1,3 @@
+ True
True
\ No newline at end of file