diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs index 50f8b6121..5fa3e91d7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return; } - // TODO: Optimize: implement all the cases with scale-specific, loopless code! + // TODO: Optimize: implement all cases with scale-specific, loopless code! this.CopyArbitraryScale(area, horizontalScale, verticalScale); } @@ -79,9 +79,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); - ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); - ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); var xLeft = new Vector4(sLeft.X); var yLeft = new Vector4(sLeft.Y); @@ -91,27 +89,33 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components var xRight = new Vector4(sRight.X); var yRight = new Vector4(sRight.Y); var zRight = new Vector4(sRight.Z); - var wRight = new Vector4(sRight.W); + var wRight = new Vector2(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; - 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, 1, ref yLeft); + AssignVector4Value(ref dTopLeft, 2, ref zLeft); + AssignVector4Value(ref dTopLeft, 3, ref wLeft); - Unsafe.As(ref dTopRight) = xRight; - 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; + 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; - 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; - 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; + 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; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value) + { + Unsafe.As(ref Unsafe.Add(ref destBase, offset)) = value; } [MethodImpl(InliningOptions.ColdPath)] diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs new file mode 100644 index 000000000..96eecc456 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs @@ -0,0 +1,82 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +{ + public class Block8x8F_CopyTo1x1 + { + private Block8x8F block; + + private Buffer2D buffer; + + private BufferArea destArea; + + [GlobalSetup] + public void Setup() + { + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + throw new InvalidOperationException("Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); + } + + this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); + this.destArea = this.buffer.GetArea(200, 100, 64, 64); + } + + [Benchmark(Baseline = true)] + public void Original() + { + ref byte selfBase = ref Unsafe.As(ref this.block); + ref byte destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride * sizeof(float); + + CopyRowImpl(ref selfBase, ref destBase, destStride, 0); + CopyRowImpl(ref selfBase, ref destBase, destStride, 1); + CopyRowImpl(ref selfBase, ref destBase, destStride, 2); + CopyRowImpl(ref selfBase, ref destBase, destStride, 3); + CopyRowImpl(ref selfBase, ref destBase, destStride, 4); + CopyRowImpl(ref selfBase, ref destBase, destStride, 5); + CopyRowImpl(ref selfBase, ref destBase, destStride, 6); + CopyRowImpl(ref selfBase, ref destBase, destStride, 7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) + { + ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); + } + + [Benchmark] + public void UseVector8() + { + ref Block8x8F s = ref this.block; + ref Vector d = ref Unsafe.As>(ref this.destArea.GetReferenceToOrigin()); + + Vector row0 = Unsafe.As>(ref s.V0L); + Vector row1 = Unsafe.As>(ref s.V1L); + Vector row2 = Unsafe.As>(ref s.V2L); + Vector row3 = Unsafe.As>(ref s.V3L); + Vector row4 = Unsafe.As>(ref s.V4L); + Vector row5 = Unsafe.As>(ref s.V5L); + Vector row6 = Unsafe.As>(ref s.V6L); + Vector row7 = Unsafe.As>(ref s.V7L); + + d = row0; + Unsafe.Add(ref d, 1) = row1; + Unsafe.Add(ref d, 2) = row2; + Unsafe.Add(ref d, 3) = row3; + Unsafe.Add(ref d, 4) = row4; + Unsafe.Add(ref d, 5) = row5; + Unsafe.Add(ref d, 6) = row6; + Unsafe.Add(ref d, 7) = row7; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs index 89de95ee9..269a3e0d8 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) { - ref Vector4 selfRight = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); @@ -257,31 +257,97 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations var wRight = new Vector4(sRight.W); Unsafe.As(ref dTopLeft) = xLeft; - 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, 1, ref yLeft); + AssignVector4Value(ref dTopLeft, 2, ref zLeft); + AssignVector4Value(ref dTopLeft, 3, ref wLeft); + Unsafe.As(ref dTopRight) = xRight; - 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; + AssignVector4Value(ref dTopRight, 1, ref yRight); + AssignVector4Value(ref dTopRight, 2, ref zRight); + AssignVector4Value(ref dTopRight, 3, ref wRight); Unsafe.As(ref dBottomLeft) = xLeft; - 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, 1, ref yLeft); + AssignVector4Value(ref dBottomLeft, 2, ref zLeft); + AssignVector4Value(ref dBottomLeft, 3, ref wLeft); Unsafe.As(ref dBottomRight) = xRight; - 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; + AssignVector4Value(ref dBottomRight, 1, ref yRight); + AssignVector4Value(ref dBottomRight, 2, ref zRight); + AssignVector4Value(ref dBottomRight, 3, ref wRight); + } + + [Benchmark] + public void UseVector4_SafeRightCorner() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.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; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value) + { + Unsafe.As(ref Unsafe.Add(ref destBase, offset)) = value; } // RESULTS: - // Method | Mean | Error | StdDev | Scaled | - // ------------ |---------:|----------:|----------:|-------:| - // Original | 88.93 ns | 0.7783 ns | 0.6899 ns | 1.00 | - // Original_V2 | 88.39 ns | 0.9426 ns | 0.8356 ns | 0.99 | - // UseVector2 | 45.63 ns | 0.4248 ns | 0.3548 ns | 0.51 | + // 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 | } } \ No newline at end of file