From 7b93ee74d5b881daa51b5c8c6dc9e657ba14f83b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 23 Jan 2024 23:26:23 +1000 Subject: [PATCH] Update benchmarks --- .../General/PixelConversion/ITestPixel.cs | 8 +- .../PixelConversion_ConvertFromRgba32.cs | 184 +++++++++++------- .../PixelConversion_ConvertFromVector4.cs | 37 ---- .../General/PixelConversion/TestArgb.cs | 95 +++++---- .../General/PixelConversion/TestRgba.cs | 69 ++++--- .../General/PixelConversion/TestRgbaVector.cs | 51 +++++ 6 files changed, 266 insertions(+), 178 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs index 8820406af6..82f0da5052 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs @@ -11,19 +11,23 @@ public interface ITestPixel { void FromRgba32(Rgba32 source); + static abstract T StaticFromRgba32(Rgba32 source); + void FromRgba32(ref Rgba32 source); void FromBytes(byte r, byte g, byte b, byte a); void FromVector4(Vector4 source); + static abstract T StaticFromVector4(Vector4 source); + void FromVector4(ref Vector4 source); Rgba32 ToRgba32(); - void CopyToRgba32(ref Rgba32 dest); + void CopyToRgba32(ref Rgba32 destination); Vector4 ToVector4(); - void CopyToVector4(ref Vector4 dest); + void CopyToVector4(ref Vector4 destination); } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs index 479079a80c..1d83b94dc8 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -13,25 +13,25 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; public abstract class PixelConversion_ConvertFromRgba32 { - internal struct ConversionRunner + internal readonly struct ConversionRunner where T : struct, ITestPixel { - public readonly T[] Dest; + public readonly T[] Destination; public readonly Rgba32[] Source; public ConversionRunner(int count) { - this.Dest = new T[count]; + this.Destination = new T[count]; this.Source = new Rgba32[count]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByRefConversion() + public readonly void RunByRefConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -41,11 +41,11 @@ public abstract class PixelConversion_ConvertFromRgba32 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunByValConversion() + public readonly void RunByValConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -55,11 +55,25 @@ public abstract class PixelConversion_ConvertFromRgba32 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RunFromBytesConversion() + public readonly void RunStaticByValConversion() { - int count = this.Dest.Length; + int count = this.Destination.Length; - ref T destBaseRef = ref this.Dest[0]; + ref T destBaseRef = ref this.Destination[0]; + ref Rgba32 sourceBaseRef = ref this.Source[0]; + + for (nuint i = 0; i < (uint)count; i++) + { + Unsafe.Add(ref destBaseRef, i) = T.StaticFromRgba32(Unsafe.Add(ref sourceBaseRef, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void RunFromBytesConversion() + { + int count = this.Destination.Length; + + ref T destBaseRef = ref this.Destination[0]; ref Rgba32 sourceBaseRef = ref this.Source[0]; for (nuint i = 0; i < (uint)count; i++) @@ -74,6 +88,8 @@ public abstract class PixelConversion_ConvertFromRgba32 internal ConversionRunner PermutedRunnerRgbaToArgb; + internal ConversionRunner RunnerRgbaToRgbaVector; + [Params(256, 2048)] public int Count { get; set; } @@ -82,34 +98,29 @@ public abstract class PixelConversion_ConvertFromRgba32 { this.CompatibleMemLayoutRunner = new ConversionRunner(this.Count); this.PermutedRunnerRgbaToArgb = new ConversionRunner(this.Count); + this.RunnerRgbaToRgbaVector = new ConversionRunner(this.Count); } } public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_ConvertFromRgba32 { [Benchmark(Baseline = true)] - public void ByRef() - { - this.CompatibleMemLayoutRunner.RunByRefConversion(); - } + public void ByRef() => this.CompatibleMemLayoutRunner.RunByRefConversion(); [Benchmark] - public void ByVal() - { - this.CompatibleMemLayoutRunner.RunByValConversion(); - } + public void ByVal() => this.CompatibleMemLayoutRunner.RunByValConversion(); [Benchmark] - public void FromBytes() - { - this.CompatibleMemLayoutRunner.RunFromBytesConversion(); - } + public void StaticByVal() => this.CompatibleMemLayoutRunner.RunStaticByValConversion(); + + [Benchmark] + public void FromBytes() => this.CompatibleMemLayoutRunner.RunFromBytesConversion(); [Benchmark] public void Inline() { ref Rgba32 sBase = ref this.CompatibleMemLayoutRunner.Source[0]; - ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Dest[0]); + ref Rgba32 dBase = ref Unsafe.As(ref this.CompatibleMemLayoutRunner.Destination[0]); for (nuint i = 0; i < (uint)this.Count; i++) { @@ -120,50 +131,46 @@ public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_Conv /* BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores - .NET SDK 8.0.100 - [Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 - DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 - - - | Method | Count | Mean | Error | StdDev | Ratio | - |---------- |------ |-----------:|--------:|--------:|------:| - | ByRef | 256 | 102.5 ns | 0.44 ns | 0.39 ns | 1.00 | - | ByVal | 256 | 102.2 ns | 0.30 ns | 0.25 ns | 1.00 | - | FromBytes | 256 | 200.5 ns | 1.01 ns | 0.90 ns | 1.96 | - | Inline | 256 | 107.0 ns | 0.90 ns | 0.84 ns | 1.04 | - | | | | | | | - | ByRef | 2048 | 770.8 ns | 3.22 ns | 2.86 ns | 1.00 | - | ByVal | 2048 | 770.3 ns | 2.05 ns | 1.92 ns | 1.00 | - | FromBytes | 2048 | 1,546.8 ns | 7.51 ns | 6.66 ns | 2.01 | - | Inline | 2048 | 797.6 ns | 2.90 ns | 2.26 ns | 1.03 | + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | + |------------ |------ |-----------:|--------:|--------:|------:| + | ByRef | 256 | 103.4 ns | 0.52 ns | 0.46 ns | 1.00 | + | ByVal | 256 | 103.3 ns | 1.48 ns | 1.38 ns | 1.00 | + | StaticByVal | 256 | 104.0 ns | 0.36 ns | 0.30 ns | 1.01 | + | FromBytes | 256 | 201.8 ns | 1.30 ns | 1.15 ns | 1.95 | + | Inline | 256 | 106.6 ns | 0.40 ns | 0.34 ns | 1.03 | + | | | | | | | + | ByRef | 2048 | 771.5 ns | 3.68 ns | 3.27 ns | 1.00 | + | ByVal | 2048 | 769.7 ns | 3.39 ns | 2.83 ns | 1.00 | + | StaticByVal | 2048 | 773.2 ns | 3.95 ns | 3.50 ns | 1.00 | + | FromBytes | 2048 | 1,555.3 ns | 9.24 ns | 8.19 ns | 2.02 | + | Inline | 2048 | 799.5 ns | 5.91 ns | 4.93 ns | 1.04 | */ } public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 { [Benchmark(Baseline = true)] - public void ByRef() - { - this.PermutedRunnerRgbaToArgb.RunByRefConversion(); - } + public void ByRef() => this.PermutedRunnerRgbaToArgb.RunByRefConversion(); [Benchmark] - public void ByVal() - { - this.PermutedRunnerRgbaToArgb.RunByValConversion(); - } + public void ByVal() => this.PermutedRunnerRgbaToArgb.RunByValConversion(); [Benchmark] - public void FromBytes() - { - this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); - } + public void StaticByVal() => this.PermutedRunnerRgbaToArgb.RunStaticByValConversion(); + + [Benchmark] + public void FromBytes() => this.PermutedRunnerRgbaToArgb.RunFromBytesConversion(); [Benchmark] public void InlineShuffle() { ref Rgba32 sBase = ref this.PermutedRunnerRgbaToArgb.Source[0]; - ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Dest[0]; + ref TestArgb dBase = ref this.PermutedRunnerRgbaToArgb.Destination[0]; for (nuint i = 0; i < (uint)this.Count; i++) { @@ -181,25 +188,64 @@ public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConver public void PixelConverter_Rgba32_ToArgb32() { Span source = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Source); - Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Dest); + Span dest = MemoryMarshal.Cast(this.PermutedRunnerRgbaToArgb.Destination); PixelConverter.FromRgba32.ToArgb32(source, dest); } /* - RESULTS: - | Method | Count | Mean | Error | StdDev | Median | Ratio | RatioSD | - |------------------------------- |------ |------------:|----------:|----------:|------------:|------:|--------:| - | ByRef | 256 | 288.84 ns | 19.601 ns | 52.319 ns | 268.10 ns | 1.00 | 0.00 | - | ByVal | 256 | 267.97 ns | 1.831 ns | 1.713 ns | 267.85 ns | 0.77 | 0.18 | - | FromBytes | 256 | 266.81 ns | 2.427 ns | 2.270 ns | 266.47 ns | 0.76 | 0.18 | - | InlineShuffle | 256 | 291.41 ns | 5.820 ns | 5.444 ns | 290.17 ns | 0.83 | 0.19 | - | PixelConverter_Rgba32_ToArgb32 | 256 | 38.62 ns | 0.431 ns | 0.403 ns | 38.68 ns | 0.11 | 0.03 | - | | | | | | | | | - | ByRef | 2048 | 2,197.69 ns | 15.826 ns | 14.804 ns | 2,197.25 ns | 1.00 | 0.00 | - | ByVal | 2048 | 2,226.81 ns | 44.266 ns | 62.054 ns | 2,197.17 ns | 1.03 | 0.04 | - | FromBytes | 2048 | 2,181.35 ns | 18.033 ns | 16.868 ns | 2,185.97 ns | 0.99 | 0.01 | - | InlineShuffle | 2048 | 2,233.10 ns | 27.673 ns | 24.531 ns | 2,229.78 ns | 1.02 | 0.01 | - | PixelConverter_Rgba32_ToArgb32 | 2048 | 139.90 ns | 2.152 ns | 3.825 ns | 138.70 ns | 0.06 | 0.00 | + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | + |------------------------------- |------ |------------:|----------:|---------:|------:|--------:| + | ByRef | 256 | 203.48 ns | 3.318 ns | 3.104 ns | 1.00 | 0.00 | + | ByVal | 256 | 201.46 ns | 2.242 ns | 1.872 ns | 0.99 | 0.02 | + | StaticByVal | 256 | 201.45 ns | 0.791 ns | 0.701 ns | 0.99 | 0.02 | + | FromBytes | 256 | 200.76 ns | 1.365 ns | 1.140 ns | 0.99 | 0.01 | + | InlineShuffle | 256 | 221.65 ns | 2.104 ns | 1.968 ns | 1.09 | 0.02 | + | PixelConverter_Rgba32_ToArgb32 | 256 | 26.23 ns | 0.277 ns | 0.231 ns | 0.13 | 0.00 | + | | | | | | | | + | ByRef | 2048 | 1,561.54 ns | 11.208 ns | 8.751 ns | 1.00 | 0.00 | + | ByVal | 2048 | 1,554.26 ns | 9.607 ns | 8.517 ns | 1.00 | 0.01 | + | StaticByVal | 2048 | 1,562.48 ns | 8.937 ns | 8.360 ns | 1.00 | 0.01 | + | FromBytes | 2048 | 1,552.68 ns | 7.445 ns | 5.812 ns | 0.99 | 0.01 | + | InlineShuffle | 2048 | 1,711.28 ns | 7.559 ns | 6.312 ns | 1.10 | 0.01 | + | PixelConverter_Rgba32_ToArgb32 | 2048 | 94.43 ns | 0.363 ns | 0.322 ns | 0.06 | 0.00 | + */ +} + +public class PixelConversion_ConvertFromRgba32_RgbaToRgbaVector : PixelConversion_ConvertFromRgba32 +{ + [Benchmark(Baseline = true)] + public void ByRef() => this.RunnerRgbaToRgbaVector.RunByRefConversion(); + + [Benchmark] + public void ByVal() => this.RunnerRgbaToRgbaVector.RunByValConversion(); + + [Benchmark] + public void StaticByVal() => this.RunnerRgbaToRgbaVector.RunStaticByValConversion(); + + /* + BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3007/23H2/2023Update/SunValley3) + 11th Gen Intel Core i7-11370H 3.30GHz, 1 CPU, 8 logical and 4 physical cores + .NET SDK 8.0.200-preview.23624.5 + [Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2 + + + | Method | Count | Mean | Error | StdDev | Ratio | RatioSD | + |------------ |------ |-----------:|---------:|---------:|------:|--------:| + | ByRef | 256 | 448.5 ns | 4.86 ns | 4.06 ns | 1.00 | 0.00 | + | ByVal | 256 | 447.0 ns | 1.55 ns | 1.21 ns | 1.00 | 0.01 | + | StaticByVal | 256 | 447.4 ns | 1.67 ns | 1.30 ns | 1.00 | 0.01 | + | | | | | | | | + | ByRef | 2048 | 3,577.7 ns | 53.80 ns | 47.69 ns | 1.00 | 0.00 | + | ByVal | 2048 | 3,590.5 ns | 43.59 ns | 36.40 ns | 1.00 | 0.02 | + | StaticByVal | 2048 | 3,604.6 ns | 16.19 ns | 14.36 ns | 1.01 | 0.01 | */ } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs index dd85d06417..57f79ba1f3 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs @@ -13,43 +13,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; public class PixelConversion_ConvertFromVector4 { - [StructLayout(LayoutKind.Sequential)] - private struct TestRgbaVector : ITestPixel - { - private Vector4 v; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) - { - this.v = p; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) - { - this.v = p; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() => this.v; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) - { - dest = this.v; - } - - public void FromRgba32(Rgba32 source) => throw new System.NotImplementedException(); - - public void FromRgba32(ref Rgba32 source) => throw new System.NotImplementedException(); - - public void FromBytes(byte r, byte g, byte b, byte a) => throw new System.NotImplementedException(); - - public Rgba32 ToRgba32() => throw new System.NotImplementedException(); - - public void CopyToRgba32(ref Rgba32 dest) => throw new System.NotImplementedException(); - } - private struct ConversionRunner where T : struct, ITestPixel { diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs index 84698a0e19..21bef5a156 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -16,26 +17,20 @@ public struct TestArgb : ITestPixel public byte G; public byte B; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 p) - { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; - } + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 p) + private TestArgb(Rgba32 source) { - this.R = p.R; - this.G = p.G; - this.B = p.B; - this.A = p.A; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromBytes(byte r, byte g, byte b, byte a) + private TestArgb(byte r, byte g, byte b, byte a) { this.R = r; this.G = g; @@ -44,50 +39,70 @@ public struct TestArgb : ITestPixel } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) + public void FromRgba32(Rgba32 source) { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) + public void FromRgba32(ref Rgba32 source) { - this.R = (byte)p.X; - this.G = (byte)p.Y; - this.B = (byte)p.Z; - this.A = (byte)p.W; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return new Rgba32(this.R, this.G, this.B, this.A); - } + public static TestArgb StaticFromRgba32(Rgba32 source) => new(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) + public void FromBytes(byte r, byte g, byte b, byte a) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + this.R = r; + this.G = g; + this.B = b; + this.A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() + public void FromVector4(Vector4 source) => this = Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestArgb StaticFromVector4(Vector4 source) => Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 source) => this = Pack(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => new(this.R, this.G, this.B, this.A); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToRgba32(ref Rgba32 destination) { - return new Vector4(this.R, this.G, this.B, this.A); + destination.R = this.R; + destination.G = this.G; + destination.B = this.B; + destination.A = this.A; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.ToVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TestArgb Pack(Vector4 vector) { - dest.X = this.R; - dest.Y = this.G; - dest.Z = this.B; - dest.W = this.A; + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs index 76449c9d95..1499fb7d3d 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs @@ -4,6 +4,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; @@ -16,17 +17,29 @@ public struct TestRgba : ITestPixel public byte B; public byte A; + private static readonly Vector4 MaxBytes = Vector128.Create(255f).AsVector4(); + private static readonly Vector4 Half = Vector128.Create(.5f).AsVector4(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(Rgba32 source) + private TestRgba(byte r, byte g, byte b, byte a) { - this = Unsafe.As(ref source); + this.R = r; + this.G = g; + this.B = b; + this.A = a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromRgba32(ref Rgba32 source) - { - this = Unsafe.As(ref source); - } + private TestRgba(Rgba32 source) => this = Unsafe.As(ref source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this = Unsafe.As(ref source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgba StaticFromRgba32(Rgba32 source) => new(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) => this = Unsafe.As(ref source); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FromBytes(byte r, byte g, byte b, byte a) @@ -37,39 +50,35 @@ public struct TestRgba : ITestPixel this.A = a; } - public void FromVector4(Vector4 source) - { - throw new System.NotImplementedException(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 source) => this = Pack(source); - public void FromVector4(ref Vector4 source) - { - throw new System.NotImplementedException(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgba StaticFromVector4(Vector4 source) => Pack(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() - { - return Unsafe.As(ref this); - } + public void FromVector4(ref Vector4 source) => this = Pack(source); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToRgba32(ref Rgba32 dest) - { - dest = Unsafe.As(ref this); - } + public Rgba32 ToRgba32() => Unsafe.As(ref this); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A) * new Vector4(1f / 255f); - } + public void CopyToRgba32(ref Rgba32 destination) => destination = Unsafe.As(ref this); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyToVector4(ref Vector4 dest) + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.ToVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TestRgba Pack(Vector4 vector) { - var tmp = new Vector4(this.R, this.G, this.B, this.A); - tmp *= new Vector4(1f / 255f); - dest = tmp; + vector *= MaxBytes; + vector += Half; + vector = Numerics.Clamp(vector, Vector4.Zero, MaxBytes); + + Vector128 result = Vector128.ConvertToInt32(vector.AsVector128()).AsByte(); + return new(result.GetElement(0), result.GetElement(4), result.GetElement(8), result.GetElement(12)); } } diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs new file mode 100644 index 0000000000..07790ae998 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgbaVector.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion; + +[StructLayout(LayoutKind.Sequential)] +public struct TestRgbaVector : ITestPixel +{ + private Vector4 v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TestRgbaVector(Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgbaVector StaticFromVector4(Vector4 source) => new(source); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 source) => this.v = source; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Vector4 ToVector4() => this.v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToVector4(ref Vector4 destination) => destination = this.v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) => this.v = source.ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TestRgbaVector StaticFromRgba32(Rgba32 source) => new(source.ToScaledVector4()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) => this.v = source.ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) => this.v = new Rgba32(r, g, b, a).ToScaledVector4(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Rgba32 ToRgba32() => Rgba32.FromScaledVector4(this.v); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void CopyToRgba32(ref Rgba32 destination) => destination = Rgba32.FromScaledVector4(this.v); +}