Browse Source

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

pull/768/head
Anton Firszov 7 years ago
parent
commit
b32694590d
  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.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<float, Vector2>(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<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;
}
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));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AssignVector4Value(ref Vector2 destBase, int offset, ref Vector4 value)
{
Unsafe.As<Vector2, Vector4>(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)]

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);
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);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dTopRight) = xRight;
AssignVector4Value(ref dTopRight, 1, ref yRight);
AssignVector4Value(ref dTopRight, 2, ref zRight);
AssignVector4Value(ref dTopRight, 3, ref wRight);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopRight, 3)) = 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);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft;
Unsafe.As<Vector2, Vector4>(ref dBottomRight) = xRight;
AssignVector4Value(ref dBottomRight, 1, ref yRight);
AssignVector4Value(ref dBottomRight, 2, ref zRight);
AssignVector4Value(ref dBottomRight, 3, ref wRight);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 1)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomRight, 2)) = zRight;
Unsafe.As<Vector2, Vector4>(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<Vector2, Vector4>(ref dTopLeft) = xLeft;
AssignVector4Value(ref dTopLeft, 1, ref yLeft);
AssignVector4Value(ref dTopLeft, 2, ref zLeft);
AssignVector4Value(ref dTopLeft, 3, ref wLeft);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(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<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dTopLeft, 6)) = 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);
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft;
Unsafe.As<Vector2, Vector4>(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<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight;
Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight;
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)]
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:
// 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 |
}
}

5
tests/ImageSharp.Sandbox46/Program.cs

@ -4,6 +4,7 @@
// </copyright>
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);

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.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<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.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)
{

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