From 79537eb16ec42795429bc8bb8ad6a3a7cfcb62e5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 8 Mar 2017 03:26:27 +0100 Subject: [PATCH] benchmarks --- .../Common/Memory/BufferPointer{T}.cs | 5 + src/ImageSharp/project.json | 1 + .../Color/Bulk/PackFromXyzw.cs | 63 +++++++++ .../Color/Bulk/PixelAccessorVirtualCopy.cs | 129 ------------------ .../ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs | 61 +++++++++ .../Color/Bulk/ToXyzw.cs | 70 ++++++++++ .../ImageSharp.Benchmarks/Image/EncodePng.cs | 23 +++- .../ImageSharp.Sandbox46.csproj | 7 +- .../Colors/BulkPixelOperationsTests.cs | 4 +- 9 files changed, 226 insertions(+), 137 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs delete mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs create mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs create mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs index fe79e064e..cfdd8e6de 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs @@ -63,6 +63,11 @@ namespace ImageSharp /// public int Offset { get; private set; } + /// + /// Gets the offset inside in bytes. + /// + public int ByteOffset => this.Offset * Unsafe.SizeOf(); + /// /// Gets the pointer to the offseted array position /// diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json index 639773377..6519a3f6b 100644 --- a/src/ImageSharp/project.json +++ b/src/ImageSharp/project.json @@ -98,6 +98,7 @@ }, "net461": { "dependencies": { + "System.Numerics.Vectors": "4.1.1", "System.Threading.Tasks.Parallel": "4.0.0" }, "frameworkAssemblies": { diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs new file mode 100644 index 000000000..1c541d28b --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -0,0 +1,63 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Benchmarks.Color.Bulk +{ + using BenchmarkDotNet.Attributes; + + using Color = ImageSharp.Color; + + public abstract class PackFromXyzw + where TColor : struct, IPixel + { + private PinnedBuffer destination; + + private PinnedBuffer source; + + [Params(16, 128, 1024)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.destination = new PinnedBuffer(this.Count); + this.source = new PinnedBuffer(this.Count * 4); + } + + [Cleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } + + [Benchmark(Baseline = true)] + public void PerElement() + { + byte[] s = this.source.Array; + TColor[] d = this.destination.Array; + + for (int i = 0; i < this.Count; i++) + { + int i4 = i * 4; + TColor c = default(TColor); + c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]); + d[i] = c; + } + } + + [Benchmark] + public void CommonBulk() + { + new BulkPixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + BulkPixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); + } + } + + public class PackFromXyzw_Color : PackFromXyzw + { + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs deleted file mode 100644 index 3df688972..000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ImageSharp.Benchmarks.Color.Bulk -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - using BenchmarkDotNet.Attributes; - - using Color = ImageSharp.Color; - - /// - /// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods - /// - public unsafe class PixelAccessorVirtualCopy - { - abstract class CopyExecutor - { - internal abstract void VirtualCopy(BufferPointer destination, BufferPointer source, int count); - } - - class UnsafeCopyExecutor : CopyExecutor - { - [MethodImpl(MethodImplOptions.NoInlining)] - internal override unsafe void VirtualCopy(BufferPointer destination, BufferPointer source, int count) - { - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); - } - } - - private PixelAccessor pixelAccessor; - - private PixelArea area; - - private CopyExecutor executor; - - [Params(64, 256, 512)] - public int Width { get; set; } - - public int Height { get; set; } = 256; - - - [Setup] - public void Setup() - { - this.pixelAccessor = new PixelAccessor(this.Width, this.Height); - this.area = new PixelArea(this.Width / 2, this.Height, ComponentOrder.Xyzw); - this.executor = new UnsafeCopyExecutor(); - } - - [Cleanup] - public void Cleanup() - { - this.pixelAccessor.Dispose(); - this.area.Dispose(); - } - - [Benchmark(Baseline = true)] - public void CopyRawUnsafeInlined() - { - uint byteCount = (uint)this.area.Width * 4; - - int targetX = this.Width / 4; - int targetY = 0; - - for (int y = 0; y < this.Height; y++) - { - byte* source = this.area.PixelBase + (y * this.area.RowStride); - byte* destination = this.GetRowPointer(targetX, targetY + y); - - Unsafe.CopyBlock(destination, source, byteCount); - } - } - - [Benchmark] - public void CopyBufferPointerUnsafeInlined() - { - uint byteCount = (uint)this.area.Width * 4; - - int targetX = this.Width / 4; - int targetY = 0; - - for (int y = 0; y < this.Height; y++) - { - BufferPointer source = this.GetAreaRow(y); - BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); - } - } - - [Benchmark] - public void CopyBufferPointerUnsafeVirtual() - { - int targetX = this.Width / 4; - int targetY = 0; - - for (int y = 0; y < this.Height; y++) - { - BufferPointer source = this.GetAreaRow(y); - BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); - this.executor.VirtualCopy(destination, source, this.area.Width); - } - } - - private byte* GetRowPointer(int x, int y) - { - return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf()); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private BufferPointer GetPixelAccessorRow(int x, int y) - { - return new BufferPointer( - this.pixelAccessor.PixelArray, - (void*)this.pixelAccessor.DataPointer, - (y * this.pixelAccessor.Width) + x - ); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private BufferPointer GetAreaRow(int y) - { - return new BufferPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs new file mode 100644 index 000000000..bc59dba4e --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -0,0 +1,61 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Benchmarks.Color.Bulk +{ + using BenchmarkDotNet.Attributes; + + using Color = ImageSharp.Color; + + public abstract class ToXyz + where TColor : struct, IPixel + { + private PinnedBuffer source; + + private PinnedBuffer destination; + + [Params(16, 128, 1024)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.source = new PinnedBuffer(this.Count); + this.destination = new PinnedBuffer(this.Count * 3); + } + + [Cleanup] + public void Cleanup() + { + this.source.Dispose(); + this.destination.Dispose(); + } + + [Benchmark(Baseline = true)] + public void PerElement() + { + TColor[] s = this.source.Array; + byte[] d = this.destination.Array; + + for (int i = 0; i < this.Count; i++) + { + TColor c = s[i]; + c.ToXyzBytes(d, i * 4); + } + } + + [Benchmark] + public void CommonBulk() + { + new BulkPixelOperations().ToXyzBytes(this.source, this.destination, this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + BulkPixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); + } + } + + public class ToXyz_Color : ToXyz + { + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs new file mode 100644 index 000000000..a4ec6f6dc --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +// ReSharper disable InconsistentNaming + +namespace ImageSharp.Benchmarks.Color.Bulk +{ + using BenchmarkDotNet.Attributes; + + using Color = ImageSharp.Color; + + public abstract class ToXyzw + where TColor : struct, IPixel + { + private PinnedBuffer source; + + private PinnedBuffer destination; + + [Params(16, 128, 1024)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.source = new PinnedBuffer(this.Count); + this.destination = new PinnedBuffer(this.Count * 4); + } + + [Cleanup] + public void Cleanup() + { + this.source.Dispose(); + this.destination.Dispose(); + } + + [Benchmark(Baseline = true)] + public void PerElement() + { + TColor[] s = this.source.Array; + byte[] d = this.destination.Array; + + for (int i = 0; i < this.Count; i++) + { + TColor c = s[i]; + c.ToXyzwBytes(d, i * 4); + } + } + + [Benchmark] + public void CommonBulk() + { + new BulkPixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + BulkPixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); + } + } + + public class ToXyzw_Color : ToXyzw + { + } + + public class ToXyzw_Argb : ToXyzw + { + } +} diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index dd1882c80..a182ccd88 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -10,6 +10,10 @@ namespace ImageSharp.Benchmarks.Image using System.IO; using BenchmarkDotNet.Attributes; + + using ImageSharp.Formats; + using ImageSharp.Quantizers; + using CoreImage = ImageSharp.Image; public class EncodePng : BenchmarkBase @@ -19,12 +23,21 @@ namespace ImageSharp.Benchmarks.Image private Image bmpDrawing; private CoreImage bmpCore; + [Params(false, true)] + public bool LargeImage { get; set; } + + [Params(false, true)] + public bool UseOctreeQuantizer { get; set; } + [Setup] public void ReadImages() { if (this.bmpStream == null) { - this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); + string path = this.LargeImage + ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" + : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; + this.bmpStream = File.OpenRead(path); this.bmpCore = new CoreImage(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); @@ -53,7 +66,13 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - this.bmpCore.SaveAsPng(memoryStream); + Quantizer quantizer = this.UseOctreeQuantizer + ? (Quantizer) + new OctreeQuantizer() + : new PaletteQuantizer(); + + PngEncoderOptions options = new PngEncoderOptions() { Quantizer = quantizer }; + this.bmpCore.SaveAsPng(memoryStream, options); } } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 4d3548c81..02318d0b4 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -206,9 +206,6 @@ - - Benchmarks\PixelAccessorVirtualCopy.cs - Tests\Colors\BulkPixelOperationsTests.cs @@ -346,7 +343,9 @@ - + + + diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index fa1b536f3..88bc4a9be 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -10,7 +10,7 @@ public class Color : BulkPixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; [Fact] public void IsSpecialImplementation() @@ -35,7 +35,7 @@ public class Argb : BulkPixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; + public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; } [Theory]