Browse Source

Even better Copy2x2. Tests: Group together & refactor profiling benchmarks

af/merge-core
Anton Firszov 8 years ago
parent
commit
c5a4ed85ff
  1. 85
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  2. 115
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs
  3. 5
      tests/ImageSharp.Sandbox46/Program.cs
  4. 23
      tests/ImageSharp.Tests/ProfilingBenchmarks/JpegBenchmarks.cs
  5. 24
      tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveBenchmarks.cs
  6. 18
      tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs

85
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -3,9 +3,9 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
// ReSharper disable UseObjectOrCollectionInitializer
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{ {
@ -62,60 +62,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin()); ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref area.GetReferenceToOrigin());
int destStride = area.Stride / 2; int destStride = area.Stride / 2;
this.WidenCopyImpl2x2(ref destBase, 0, destStride); this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
this.WidenCopyImpl2x2(ref destBase, 1, destStride); this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
this.WidenCopyImpl2x2(ref destBase, 2, destStride); this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
this.WidenCopyImpl2x2(ref destBase, 3, destStride); this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
this.WidenCopyImpl2x2(ref destBase, 4, destStride); this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
this.WidenCopyImpl2x2(ref destBase, 5, destStride); this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
this.WidenCopyImpl2x2(ref destBase, 6, destStride); this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
this.WidenCopyImpl2x2(ref destBase, 7, destStride); this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); int offset = 2 * row * destStride;
ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + 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<Vector2, Vector4>(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<Vector2, Vector4>(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)] var xyLeft = new Vector4(sLeft.X);
private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value) xyLeft.Z = sLeft.Y;
{ xyLeft.W = sLeft.Y;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)) = value;
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)] [MethodImpl(InliningOptions.ColdPath)]

115
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); var wRight = new Vector4(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft; Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
AssignVector4Value(ref dTopLeft, 1, ref yLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
AssignVector4Value(ref dTopLeft, 2, ref zLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
AssignVector4Value(ref dTopLeft, 3, ref wLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight; Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
AssignVector4Value(ref dTopRight, 1, ref yRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
AssignVector4Value(ref dTopRight, 2, ref zRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
AssignVector4Value(ref dTopRight, 3, ref wRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft; Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
AssignVector4Value(ref dBottomLeft, 1, ref yLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
AssignVector4Value(ref dBottomLeft, 2, ref zLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
AssignVector4Value(ref dBottomLeft, 3, ref wLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight; Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
AssignVector4Value(ref dBottomRight, 1, ref yRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
AssignVector4Value(ref dBottomRight, 2, ref zRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
AssignVector4Value(ref dBottomRight, 3, ref wRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 3)) = wRight;
} }
[Benchmark] [Benchmark]
@ -315,39 +315,90 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
var wRight = new Vector2(sRight.W); var wRight = new Vector2(sRight.W);
Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft; Unsafe.As<Vector2, Vector4>(ref dTopLeft) = xLeft;
AssignVector4Value(ref dTopLeft, 1, ref yLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
AssignVector4Value(ref dTopLeft, 2, ref zLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
AssignVector4Value(ref dTopLeft, 3, ref wLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
AssignVector4Value(ref dTopLeft, 4, ref xRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
AssignVector4Value(ref dTopLeft, 5, ref yRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
AssignVector4Value(ref dTopLeft, 6, ref zRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = zRight;
Unsafe.Add(ref dTopLeft, 7) = wRight; Unsafe.Add(ref dTopLeft, 7) = wRight;
Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft; Unsafe.As<Vector2, Vector4>(ref dBottomLeft) = xLeft;
AssignVector4Value(ref dBottomLeft, 1, ref yLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
AssignVector4Value(ref dBottomLeft, 2, ref zLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
AssignVector4Value(ref dBottomLeft, 3, ref wLeft); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
AssignVector4Value(ref dBottomLeft, 4, ref xRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
AssignVector4Value(ref dBottomLeft, 5, ref yRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
AssignVector4Value(ref dBottomLeft, 6, ref zRight); Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
Unsafe.Add(ref dBottomLeft, 7) = wRight; Unsafe.Add(ref dBottomLeft, 7) = wRight;
} }
[Benchmark]
public void UseVector4_V2()
{
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(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)] [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<Vector2, Vector4>(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<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset));
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(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: // RESULTS:
// Method | Mean | Error | StdDev | Scaled | ScaledSD | // Method | Mean | Error | StdDev | Scaled | ScaledSD |
// --------------------------- |---------:|----------:|----------:|-------:|---------:| // --------------------------- |---------:|----------:|----------:|-------:|---------:|
// Original | 93.78 ns | 1.8419 ns | 1.9708 ns | 1.00 | 0.00 | // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 |
// Original_V2 | 89.85 ns | 0.8809 ns | 0.7356 ns | 0.96 | 0.02 | // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 |
// UseVector2 | 81.81 ns | 0.4441 ns | 0.3937 ns | 0.87 | 0.02 | // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 |
// UseVector4 | 55.74 ns | 0.3674 ns | 0.3068 ns | 0.59 | 0.01 | // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 |
// UseVector4_SafeRightCorner | 55.70 ns | 0.3239 ns | 0.2705 ns | 0.59 | 0.01 | // 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 |
} }
} }

5
tests/ImageSharp.Sandbox46/Program.cs

@ -4,6 +4,7 @@
// </copyright> // </copyright>
using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations;
using SixLabors.ImageSharp.Tests.ProfilingBenchmarks;
namespace SixLabors.ImageSharp.Sandbox46 namespace SixLabors.ImageSharp.Sandbox46
{ {
@ -62,8 +63,8 @@ namespace SixLabors.ImageSharp.Sandbox46
private static void RunDecodeJpegProfilingTests() private static void RunDecodeJpegProfilingTests()
{ {
Console.WriteLine("RunDecodeJpegProfilingTests..."); Console.WriteLine("RunDecodeJpegProfilingTests...");
var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput()); var benchmarks = new JpegBenchmarks(new ConsoleOutput());
foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) foreach (object[] data in JpegBenchmarks.DecodeJpegData)
{ {
string fileName = (string)data[0]; string fileName = (string)data[0];
benchmarks.DecodeJpeg(fileName); benchmarks.DecodeJpeg(fileName);

23
tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs → tests/ImageSharp.Tests/ProfilingBenchmarks/JpegBenchmarks.cs

@ -13,11 +13,11 @@ using SixLabors.ImageSharp.PixelFormats;
using Xunit; using Xunit;
using Xunit.Abstractions; 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) : base(output)
{ {
} }
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Jpeg444,
}; };
[Theory] // Benchmark, enable manually [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
[MemberData(nameof(DecodeJpegData))] [MemberData(nameof(DecodeJpegData))]
public void DecodeJpeg(string fileName) public void DecodeJpeg(string fileName)
{ {
@ -69,11 +69,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
} }
// Benchmark, enable manually! // Benchmark, enable manually!
// [Theory] [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
// [InlineData(1, 75, JpegSubsample.Ratio420)] [InlineData(1, 75, JpegSubsample.Ratio420)]
// [InlineData(30, 75, JpegSubsample.Ratio420)] [InlineData(30, 75, JpegSubsample.Ratio420)]
// [InlineData(30, 75, JpegSubsample.Ratio444)] [InlineData(30, 75, JpegSubsample.Ratio444)]
// [InlineData(30, 100, JpegSubsample.Ratio444)] [InlineData(30, 100, JpegSubsample.Ratio444)]
public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample)
{ {
// do not run this on CI even by accident // do not run this on CI even by accident
@ -107,6 +107,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
$@"Encode {testFiles.Length} images" $@"Encode {testFiles.Length} images"
); );
} }
foreach (Image<Rgba32> image in testImages)
{
image.Dispose();
}
} }
} }

24
tests/ImageSharp.Tests/ProfilingBenchmarks.cs → 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.ImageSharp.Processing;
using SixLabors.Primitives;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks
{ {
public class ProfilingBenchmarks : MeasureFixture public class LoadResizeSaveBenchmarks : MeasureFixture
{ {
public const string SkipProfilingTests = public LoadResizeSaveBenchmarks(ITestOutputHelper output)
#if true
null;
#else
"Profiling benchmark, enable manually!";
#endif
public ProfilingBenchmarks(ITestOutputHelper output)
: base(output) : base(output)
{ {
} }
[Theory(Skip = SkipProfilingTests)] [Theory(Skip = ProfilingSetup.SkipProfilingTests)]
[InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)]
public void LoadResizeSave(string imagePath) public void LoadResizeSave(string imagePath)
{ {

18
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
}
}
Loading…
Cancel
Save