From b32694590da020985d443254a0305be21bb5f8a5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 3 Nov 2018 22:05:06 +0100 Subject: [PATCH] Even better Copy2x2. Tests: Group together & refactor profiling benchmarks --- .../Jpeg/Components/Block8x8F.CopyTo.cs | 85 ++++++------- .../BlockOperations/Block8x8F_CopyTo2x2.cs | 115 +++++++++++++----- tests/ImageSharp.Sandbox46/Program.cs | 5 +- .../JpegBenchmarks.cs} | 23 ++-- .../LoadResizeSaveBenchmarks.cs} | 24 ++-- .../ProfilingBenchmarks/ProfilingSetup.cs | 18 +++ 6 files changed, 164 insertions(+), 106 deletions(-) rename tests/ImageSharp.Tests/{Formats/Jpg/JpegProfilingBenchmarks.cs => ProfilingBenchmarks/JpegBenchmarks.cs} (84%) rename tests/ImageSharp.Tests/{ProfilingBenchmarks.cs => ProfilingBenchmarks/LoadResizeSaveBenchmarks.cs} (67%) create mode 100644 tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs index 5fa3e91d7..6bf9c8483 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs @@ -3,9 +3,9 @@ using System.Numerics; using System.Runtime.CompilerServices; - using SixLabors.ImageSharp.Memory; +// ReSharper disable UseObjectOrCollectionInitializer // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Components { @@ -62,60 +62,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); int destStride = area.Stride / 2; - this.WidenCopyImpl2x2(ref destBase, 0, destStride); - this.WidenCopyImpl2x2(ref destBase, 1, destStride); - this.WidenCopyImpl2x2(ref destBase, 2, destStride); - this.WidenCopyImpl2x2(ref destBase, 3, destStride); - this.WidenCopyImpl2x2(ref destBase, 4, destStride); - this.WidenCopyImpl2x2(ref destBase, 5, destStride); - this.WidenCopyImpl2x2(ref destBase, 6, destStride); - this.WidenCopyImpl2x2(ref destBase, 7, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 2, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 3, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 4, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 5, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 6, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 7, destStride); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WidenCopyImpl2x2(ref Vector2 destBase, int row, int destStride) + private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) { ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); - ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); - - var xLeft = new Vector4(sLeft.X); - var yLeft = new Vector4(sLeft.Y); - var zLeft = new Vector4(sLeft.Z); - var wLeft = new Vector4(sLeft.W); - - var xRight = new Vector4(sRight.X); - var yRight = new Vector4(sRight.Y); - var zRight = new Vector4(sRight.Z); - var wRight = new Vector2(sRight.W); - - Unsafe.As(ref dTopLeft) = xLeft; - AssignVector4Value(ref dTopLeft, 1, ref yLeft); - AssignVector4Value(ref dTopLeft, 2, ref zLeft); - AssignVector4Value(ref dTopLeft, 3, ref wLeft); - - AssignVector4Value(ref dTopLeft, 4, ref xRight); - AssignVector4Value(ref dTopLeft, 5, ref yRight); - AssignVector4Value(ref dTopLeft, 6, ref zRight); - Unsafe.Add(ref dTopLeft, 7) = wRight; - - Unsafe.As(ref dBottomLeft) = xLeft; - AssignVector4Value(ref dBottomLeft, 1, ref yLeft); - AssignVector4Value(ref dBottomLeft, 2, ref zLeft); - AssignVector4Value(ref dBottomLeft, 3, ref wLeft); - - AssignVector4Value(ref dBottomLeft, 4, ref xRight); - AssignVector4Value(ref dBottomLeft, 5, ref yRight); - AssignVector4Value(ref dBottomLeft, 6, ref zRight); - Unsafe.Add(ref dBottomLeft, 7) = wRight; - } + int offset = 2 * row * destStride; + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value) - { - Unsafe.As(ref Unsafe.Add(ref destBase, offset)) = value; + var xyLeft = new Vector4(sLeft.X); + xyLeft.Z = sLeft.Y; + xyLeft.W = sLeft.Y; + + var zwLeft = new Vector4(sLeft.Z); + zwLeft.Z = sLeft.W; + zwLeft.W = sLeft.W; + + var xyRight = new Vector4(sRight.X); + xyRight.Z = sRight.Y; + xyRight.W = sRight.Y; + + var zwRight = new Vector4(sRight.Z); + zwRight.Z = sRight.W; + zwRight.W = sRight.W; + + dTopLeft = xyLeft; + Unsafe.Add(ref dTopLeft, 1) = zwLeft; + Unsafe.Add(ref dTopLeft, 2) = xyRight; + Unsafe.Add(ref dTopLeft, 3) = zwRight; + + dBottomLeft = xyLeft; + Unsafe.Add(ref dBottomLeft, 1) = zwLeft; + Unsafe.Add(ref dBottomLeft, 2) = xyRight; + Unsafe.Add(ref dBottomLeft, 3) = zwRight; } [MethodImpl(InliningOptions.ColdPath)] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 269a3e0d8..65176af5b 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -257,24 +257,24 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations var wRight = new Vector4(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; - AssignVector4Value(ref dTopLeft, 1, ref yLeft); - AssignVector4Value(ref dTopLeft, 2, ref zLeft); - AssignVector4Value(ref dTopLeft, 3, ref wLeft); - + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; + Unsafe.As(ref dTopRight) = xRight; - AssignVector4Value(ref dTopRight, 1, ref yRight); - AssignVector4Value(ref dTopRight, 2, ref zRight); - AssignVector4Value(ref dTopRight, 3, ref wRight); + Unsafe.As(ref Unsafe.Add(ref dTopRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 3)) = wRight; Unsafe.As(ref dBottomLeft) = xLeft; - AssignVector4Value(ref dBottomLeft, 1, ref yLeft); - AssignVector4Value(ref dBottomLeft, 2, ref zLeft); - AssignVector4Value(ref dBottomLeft, 3, ref wLeft); + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; Unsafe.As(ref dBottomRight) = xRight; - AssignVector4Value(ref dBottomRight, 1, ref yRight); - AssignVector4Value(ref dBottomRight, 2, ref zRight); - AssignVector4Value(ref dBottomRight, 3, ref wRight); + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 3)) = wRight; } [Benchmark] @@ -315,39 +315,90 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations var wRight = new Vector2(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; - AssignVector4Value(ref dTopLeft, 1, ref yLeft); - AssignVector4Value(ref dTopLeft, 2, ref zLeft); - AssignVector4Value(ref dTopLeft, 3, ref wLeft); + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; - AssignVector4Value(ref dTopLeft, 4, ref xRight); - AssignVector4Value(ref dTopLeft, 5, ref yRight); - AssignVector4Value(ref dTopLeft, 6, ref zRight); + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 6)) = zRight; Unsafe.Add(ref dTopLeft, 7) = wRight; Unsafe.As(ref dBottomLeft) = xLeft; - AssignVector4Value(ref dBottomLeft, 1, ref yLeft); - AssignVector4Value(ref dBottomLeft, 2, ref zLeft); - AssignVector4Value(ref dBottomLeft, 3, ref wLeft); + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; - AssignVector4Value(ref dBottomLeft, 4, ref xRight); - AssignVector4Value(ref dBottomLeft, 5, ref yRight); - AssignVector4Value(ref dBottomLeft, 6, ref zRight); + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight; Unsafe.Add(ref dBottomLeft, 7) = wRight; } + + [Benchmark] + public void UseVector4_V2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value) + private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) { - Unsafe.As(ref Unsafe.Add(ref destBase, offset)) = value; + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + int offset = 2 * row * destStride; + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); + + var xyLeft = new Vector4(sLeft.X); + xyLeft.Z = sLeft.Y; + xyLeft.W = sLeft.Y; + + var zwLeft = new Vector4(sLeft.Z); + zwLeft.Z = sLeft.W; + zwLeft.W = sLeft.W; + + var xyRight = new Vector4(sRight.X); + xyRight.Z = sRight.Y; + xyRight.W = sRight.Y; + + var zwRight = new Vector4(sRight.Z); + zwRight.Z = sRight.W; + zwRight.W = sRight.W; + + dTopLeft = xyLeft; + Unsafe.Add(ref dTopLeft, 1) = zwLeft; + Unsafe.Add(ref dTopLeft, 2) = xyRight; + Unsafe.Add(ref dTopLeft, 3) = zwRight; + + dBottomLeft = xyLeft; + Unsafe.Add(ref dBottomLeft, 1) = zwLeft; + Unsafe.Add(ref dBottomLeft, 2) = xyRight; + Unsafe.Add(ref dBottomLeft, 3) = zwRight; } // RESULTS: // Method | Mean | Error | StdDev | Scaled | ScaledSD | // --------------------------- |---------:|----------:|----------:|-------:|---------:| - // Original | 93.78 ns | 1.8419 ns | 1.9708 ns | 1.00 | 0.00 | - // Original_V2 | 89.85 ns | 0.8809 ns | 0.7356 ns | 0.96 | 0.02 | - // UseVector2 | 81.81 ns | 0.4441 ns | 0.3937 ns | 0.87 | 0.02 | - // UseVector4 | 55.74 ns | 0.3674 ns | 0.3068 ns | 0.59 | 0.01 | - // UseVector4_SafeRightCorner | 55.70 ns | 0.3239 ns | 0.2705 ns | 0.59 | 0.01 | + // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 | + // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 | + // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 | + // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 | + // UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 | + // UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 | } } \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 3a3a7d31c..c0bb25a1b 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -4,6 +4,7 @@ // using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; +using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; namespace SixLabors.ImageSharp.Sandbox46 { @@ -62,8 +63,8 @@ namespace SixLabors.ImageSharp.Sandbox46 private static void RunDecodeJpegProfilingTests() { Console.WriteLine("RunDecodeJpegProfilingTests..."); - var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput()); - foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) + var benchmarks = new JpegBenchmarks(new ConsoleOutput()); + foreach (object[] data in JpegBenchmarks.DecodeJpegData) { string fileName = (string)data[0]; benchmarks.DecodeJpeg(fileName); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegBenchmarks.cs similarity index 84% rename from tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/ProfilingBenchmarks/JpegBenchmarks.cs index 88959bfab..3d439c5ce 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegBenchmarks.cs @@ -13,11 +13,11 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests.Formats.Jpg +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks { - public class JpegProfilingBenchmarks : MeasureFixture + public class JpegBenchmarks : MeasureFixture { - public JpegProfilingBenchmarks(ITestOutputHelper output) + public JpegBenchmarks(ITestOutputHelper output) : base(output) { } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, }; - [Theory] // Benchmark, enable manually + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] [MemberData(nameof(DecodeJpegData))] public void DecodeJpeg(string fileName) { @@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } // Benchmark, enable manually! - // [Theory] - // [InlineData(1, 75, JpegSubsample.Ratio420)] - // [InlineData(30, 75, JpegSubsample.Ratio420)] - // [InlineData(30, 75, JpegSubsample.Ratio444)] - // [InlineData(30, 100, JpegSubsample.Ratio444)] + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] + [InlineData(1, 75, JpegSubsample.Ratio420)] + [InlineData(30, 75, JpegSubsample.Ratio420)] + [InlineData(30, 75, JpegSubsample.Ratio444)] + [InlineData(30, 100, JpegSubsample.Ratio444)] public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) { // do not run this on CI even by accident @@ -107,6 +107,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg $@"Encode {testFiles.Length} images" ); } + + foreach (Image image in testImages) + { + image.Dispose(); + } } } diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveBenchmarks.cs similarity index 67% rename from tests/ImageSharp.Tests/ProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveBenchmarks.cs index bc9b2a947..306072276 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveBenchmarks.cs @@ -1,31 +1,23 @@ -using System.IO; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks { - public class ProfilingBenchmarks : MeasureFixture + public class LoadResizeSaveBenchmarks : MeasureFixture { - public const string SkipProfilingTests = -#if true - null; -#else - "Profiling benchmark, enable manually!"; -#endif - - - public ProfilingBenchmarks(ITestOutputHelper output) + public LoadResizeSaveBenchmarks(ITestOutputHelper output) : base(output) { } - [Theory(Skip = SkipProfilingTests)] + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] public void LoadResizeSave(string imagePath) { diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs new file mode 100644 index 000000000..267c70219 --- /dev/null +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// Uncomment to enable local profiling benchmarks. DO NOT PUSH TO MAIN! +#define PROFILING + +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks +{ + public static class ProfilingSetup + { + public const string SkipProfilingTests = +#if PROFILING + null; +#else + "Profiling benchmark, enable manually!"; +#endif + } +} \ No newline at end of file