From 55ac40327ad6a6e516bd3f32d9d3d42bf508216d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 3 Nov 2018 17:52:55 +0100 Subject: [PATCH] reimplement Block8x8F.CopyTo2x2 --- .../Jpeg/Components/Block8x8F.CopyTo.cs | 166 +++++----- .../BlockOperations/Block8x8F_CopyTo2x2.cs | 287 ++++++++++++++++++ .../BlockOperations}/Block8x8F_DivideRound.cs | 6 +- .../Block8x8F_LoadFromInt16.cs | 9 +- .../Jpeg/BlockOperations}/Block8x8F_Round.cs | 7 +- .../Jpg/Block8x8FTests.CopyToBufferArea.cs | 21 +- 6 files changed, 390 insertions(+), 106 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs rename tests/ImageSharp.Benchmarks/{General => Codecs/Jpeg/BlockOperations}/Block8x8F_DivideRound.cs (96%) rename tests/ImageSharp.Benchmarks/{General => Codecs/Jpeg/BlockOperations}/Block8x8F_LoadFromInt16.cs (83%) rename tests/ImageSharp.Benchmarks/{General => Codecs/Jpeg/BlockOperations}/Block8x8F_Round.cs (90%) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs index b7dd125a88..50f8b61212 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs @@ -12,55 +12,32 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components internal partial struct Block8x8F { /// - /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical. + /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors. /// + [MethodImpl(InliningOptions.ShortMethod)] public void CopyTo(in BufferArea area, int horizontalScale, int verticalScale) { if (horizontalScale == 1 && verticalScale == 1) { - this.CopyTo(area); + this.Copy1x1Scale(area); return; } - else if (horizontalScale == 2 && verticalScale == 2) + + if (horizontalScale == 2 && verticalScale == 2) { - this.CopyTo2x2(area); + this.Copy2x2Scale(area); return; } - ref float destBase = ref area.GetReferenceToOrigin(); - - // TODO: Optimize: implement all the cases with loopless special code! (T4?) - for (int y = 0; y < 8; y++) - { - int yy = y * verticalScale; - int y8 = y * 8; - - for (int x = 0; x < 8; x++) - { - int xx = x * horizontalScale; - - float value = this[y8 + x]; - - for (int i = 0; i < verticalScale; i++) - { - int baseIdx = ((yy + i) * area.Stride) + xx; - - for (int j = 0; j < horizontalScale; j++) - { - // area[xx + j, yy + i] = value; - Unsafe.Add(ref destBase, baseIdx + j) = value; - } - } - } - } + // TODO: Optimize: implement all the cases with scale-specific, loopless code! + this.CopyArbitraryScale(area, horizontalScale, verticalScale); } - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(in BufferArea area) + public void Copy1x1Scale(in BufferArea destination) { ref byte selfBase = ref Unsafe.As(ref this); - ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); - int destStride = area.Stride * sizeof(float); + ref byte destBase = ref Unsafe.As(ref destination.GetReferenceToOrigin()); + int destStride = destination.Stride * sizeof(float); CopyRowImpl(ref selfBase, ref destBase, destStride, 0); CopyRowImpl(ref selfBase, ref destBase, destStride, 1); @@ -80,10 +57,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); } - private void CopyTo2x2(in BufferArea area) + private void Copy2x2Scale(in BufferArea area) { - ref float destBase = ref area.GetReferenceToOrigin(); - int destStride = area.Stride; + 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); @@ -96,60 +73,75 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WidenCopyImpl2x2(ref float destBase, int row, int destStride) + private void WidenCopyImpl2x2(ref Vector2 destBase, int row, int destStride) { - ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); - ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); - - Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; - Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + 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 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); + 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 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; + + 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; + + 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; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl(ref Vector4 s, ref float destBase) + [MethodImpl(InliningOptions.ColdPath)] + private void CopyArbitraryScale(BufferArea area, int horizontalScale, int verticalScale) { - Unsafe.Add(ref destBase, 0) = s.X; - Unsafe.Add(ref destBase, 1) = s.X; - Unsafe.Add(ref destBase, 2) = s.Y; - Unsafe.Add(ref destBase, 3) = s.Y; - Unsafe.Add(ref destBase, 4) = s.Z; - Unsafe.Add(ref destBase, 5) = s.Z; - Unsafe.Add(ref destBase, 6) = s.W; - Unsafe.Add(ref destBase, 7) = s.W; + ref float destBase = ref area.GetReferenceToOrigin(); + + for (int y = 0; y < 8; y++) + { + int yy = y * verticalScale; + int y8 = y * 8; + + for (int x = 0; x < 8; x++) + { + int xx = x * horizontalScale; + + float value = this[y8 + x]; + + for (int i = 0; i < verticalScale; i++) + { + int baseIdx = ((yy + i) * area.Stride) + xx; + + for (int j = 0; j < horizontalScale; j++) + { + // area[xx + j, yy + i] = value; + Unsafe.Add(ref destBase, baseIdx + j) = value; + } + } + } + } } } } \ 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 new file mode 100644 index 0000000000..89de95ee94 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -0,0 +1,287 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Memory; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +{ + public class Block8x8F_CopyTo2x2 + { + private Block8x8F block; + + private Buffer2D buffer; + + private BufferArea destArea; + + [GlobalSetup] + public void Setup() + { + this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); + this.destArea = this.buffer.GetArea(200, 100, 128, 128); + } + + [Benchmark(Baseline = true)] + public void Original() + { + ref float destBase = ref this.destArea.GetReferenceToOrigin(); + int destStride = this.destArea.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 7, destStride); + } + + [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 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; + Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + } + + [Benchmark] + public void Original_V2() + { + ref float destBase = ref this.destArea.GetReferenceToOrigin(); + int destStride = this.destArea.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref dest0, 0) = selfLeft.X; + Unsafe.Add(ref dest0, 1) = selfLeft.X; + Unsafe.Add(ref dest0, 2) = selfLeft.Y; + Unsafe.Add(ref dest0, 3) = selfLeft.Y; + Unsafe.Add(ref dest0, 4) = selfLeft.Z; + Unsafe.Add(ref dest0, 5) = selfLeft.Z; + Unsafe.Add(ref dest0, 6) = selfLeft.W; + Unsafe.Add(ref dest0, 7) = selfLeft.W; + + ref float dest1 = ref Unsafe.Add(ref dest0, 8); + + Unsafe.Add(ref dest1, 0) = selfRight.X; + Unsafe.Add(ref dest1, 1) = selfRight.X; + Unsafe.Add(ref dest1, 2) = selfRight.Y; + Unsafe.Add(ref dest1, 3) = selfRight.Y; + Unsafe.Add(ref dest1, 4) = selfRight.Z; + Unsafe.Add(ref dest1, 5) = selfRight.Z; + Unsafe.Add(ref dest1, 6) = selfRight.W; + Unsafe.Add(ref dest1, 7) = selfRight.W; + + ref float dest2 = ref Unsafe.Add(ref dest0, destStride); + + Unsafe.Add(ref dest2, 0) = selfLeft.X; + Unsafe.Add(ref dest2, 1) = selfLeft.X; + Unsafe.Add(ref dest2, 2) = selfLeft.Y; + Unsafe.Add(ref dest2, 3) = selfLeft.Y; + Unsafe.Add(ref dest2, 4) = selfLeft.Z; + Unsafe.Add(ref dest2, 5) = selfLeft.Z; + Unsafe.Add(ref dest2, 6) = selfLeft.W; + Unsafe.Add(ref dest2, 7) = selfLeft.W; + + ref float dest3 = ref Unsafe.Add(ref dest2, 8); + + Unsafe.Add(ref dest3, 0) = selfRight.X; + Unsafe.Add(ref dest3, 1) = selfRight.X; + Unsafe.Add(ref dest3, 2) = selfRight.Y; + Unsafe.Add(ref dest3, 3) = selfRight.Y; + Unsafe.Add(ref dest3, 4) = selfRight.Z; + Unsafe.Add(ref dest3, 5) = selfRight.Z; + Unsafe.Add(ref dest3, 6) = selfRight.W; + Unsafe.Add(ref dest3, 7) = selfRight.W; + } + + [Benchmark] + public void UseVector2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector2(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 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 Vector2(sLeft.X); + var yLeft = new Vector2(sLeft.Y); + var zLeft = new Vector2(sLeft.Z); + var wLeft = new Vector2(sLeft.W); + + var xRight = new Vector2(sRight.X); + var yRight = new Vector2(sRight.Y); + var zRight = new Vector2(sRight.Z); + var wRight = new Vector2(sRight.W); + + dTopLeft = xLeft; + Unsafe.Add(ref dTopLeft, 1) = yLeft; + Unsafe.Add(ref dTopLeft, 2) = zLeft; + Unsafe.Add(ref dTopLeft, 3) = wLeft; + + dTopRight = xRight; + Unsafe.Add(ref dTopRight, 1) = yRight; + Unsafe.Add(ref dTopRight, 2) = zRight; + Unsafe.Add(ref dTopRight, 3) = wRight; + + dBottomLeft = xLeft; + Unsafe.Add(ref dBottomLeft, 1) = yLeft; + Unsafe.Add(ref dBottomLeft, 2) = zLeft; + Unsafe.Add(ref dBottomLeft, 3) = wLeft; + + dBottomRight = xRight; + Unsafe.Add(ref dBottomRight, 1) = yRight; + Unsafe.Add(ref dBottomRight, 2) = zRight; + Unsafe.Add(ref dBottomRight, 3) = wRight; + } + + [Benchmark] + public void UseVector4() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4(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 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); + 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 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; + + 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; + + 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; + } + + // 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 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs similarity index 96% rename from tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs index fcc5f9a592..5502475d43 100644 --- a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations { /// /// The goal of this benchmark is to measure the following Jpeg-related scenario: @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.General private static readonly Vector4 MinusOne = new Vector4(-1); private static readonly Vector4 Half = new Vector4(0.5f); - private Block8x8F inputDividend = default(Block8x8F); - private Block8x8F inputDivisior = default(Block8x8F); + private Block8x8F inputDividend; + private Block8x8F inputDivisior; [GlobalSetup] public void Setup() diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs similarity index 83% rename from tests/ImageSharp.Benchmarks/General/Block8x8F_LoadFromInt16.cs rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs index 34847148bf..29ee402a00 100644 --- a/tests/ImageSharp.Benchmarks/General/Block8x8F_LoadFromInt16.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs @@ -1,11 +1,16 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System; using System.Numerics; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations { public class Block8x8F_LoadFromInt16 { diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs similarity index 90% rename from tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 200af64c25..c7b5802c4f 100644 --- a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -1,4 +1,7 @@ -// ReSharper disable InconsistentNaming +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming using System; using System.Numerics; @@ -8,7 +11,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations { public class Block8x8F_Round { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index c720fdd4a7..3f426e232d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -11,10 +11,11 @@ using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - public partial class Block8x8FTests : JpegFixture + public partial class Block8x8FTests { public class CopyToBufferArea : JpegFixture { @@ -37,17 +38,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. - [Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] - //[Fact] - public void Unscaled() + [Fact] + public void Copy1x1Scale() { Block8x8F block = CreateRandomFloatBlock(0, 100); - using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20)) + using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20)) { BufferArea area = buffer.GetArea(5, 10, 8, 8); - block.CopyTo(area); + block.Copy1x1Scale(area); Assert.Equal(block[0, 0], buffer[5, 10]); Assert.Equal(block[1, 0], buffer[6, 10]); @@ -59,22 +58,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. - [Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] - //[Theory] + [Theory] [InlineData(1, 1)] [InlineData(1, 2)] [InlineData(2, 1)] [InlineData(2, 2)] [InlineData(4, 2)] [InlineData(4, 4)] - public void Scaled(int horizontalFactor, int verticalFactor) + public void CopyTo(int horizontalFactor, int verticalFactor) { Block8x8F block = CreateRandomFloatBlock(0, 100); var start = new Point(50, 50); - using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100)) + using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100)) { BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor);