mirror of https://github.com/SixLabors/ImageSharp
4 changed files with 373 additions and 14 deletions
@ -0,0 +1,206 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Benchmarks.General |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class PixelConversion_ConvertFromRgba32 |
||||
|
{ |
||||
|
interface ITestPixel<T> |
||||
|
where T : struct, ITestPixel<T> |
||||
|
{ |
||||
|
void FromRgba32(Rgba32 source); |
||||
|
|
||||
|
void FromRgba32(ref Rgba32 source); |
||||
|
|
||||
|
void FromBytes(byte r, byte g, byte b, byte a); |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
struct TestArgb : ITestPixel<TestArgb> |
||||
|
{ |
||||
|
private byte a, r, g, 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; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromRgba32(ref Rgba32 p) |
||||
|
{ |
||||
|
this.r = p.R; |
||||
|
this.g = p.G; |
||||
|
this.b = p.B; |
||||
|
this.a = p.A; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromBytes(byte r, byte g, byte b, byte a) |
||||
|
{ |
||||
|
this.r = r; |
||||
|
this.g = g; |
||||
|
this.b = b; |
||||
|
this.a = a; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
struct TestRgba : ITestPixel<TestRgba> |
||||
|
{ |
||||
|
private byte r, g, b, a; |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromRgba32(Rgba32 source) |
||||
|
{ |
||||
|
this = Unsafe.As<Rgba32, TestRgba>(ref source); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromRgba32(ref Rgba32 source) |
||||
|
{ |
||||
|
this = Unsafe.As<Rgba32, TestRgba>(ref source); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromBytes(byte r, byte g, byte b, byte a) |
||||
|
{ |
||||
|
this.r = r; |
||||
|
this.g = g; |
||||
|
this.b = b; |
||||
|
this.a = a; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct ConversionRunner<T> |
||||
|
where T : struct, ITestPixel<T> |
||||
|
{ |
||||
|
private T[] dest; |
||||
|
|
||||
|
private Rgba32[] source; |
||||
|
|
||||
|
public ConversionRunner(int count) |
||||
|
{ |
||||
|
this.dest = new T[count]; |
||||
|
this.source = new Rgba32[count]; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void RunByRefConversion() |
||||
|
{ |
||||
|
int count = this.dest.Length; |
||||
|
|
||||
|
ref T destBaseRef = ref this.dest[0]; |
||||
|
ref Rgba32 sourceBaseRef = ref this.source[0]; |
||||
|
|
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref destBaseRef, i).FromRgba32(ref Unsafe.Add(ref sourceBaseRef, i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void RunByValConversion() |
||||
|
{ |
||||
|
int count = this.dest.Length; |
||||
|
|
||||
|
ref T destBaseRef = ref this.dest[0]; |
||||
|
ref Rgba32 sourceBaseRef = ref this.source[0]; |
||||
|
|
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref destBaseRef, i).FromRgba32(Unsafe.Add(ref sourceBaseRef, i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void RunFromBytesConversion() |
||||
|
{ |
||||
|
int count = this.dest.Length; |
||||
|
|
||||
|
ref T destBaseRef = ref this.dest[0]; |
||||
|
ref Rgba32 sourceBaseRef = ref this.source[0]; |
||||
|
|
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ref Rgba32 s = ref Unsafe.Add(ref sourceBaseRef, i); |
||||
|
Unsafe.Add(ref destBaseRef, i).FromBytes(s.R, s.G, s.B, s.A); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ConversionRunner<TestRgba> compatibleMemLayoutRunner; |
||||
|
|
||||
|
private ConversionRunner<TestArgb> permutedRunner; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.compatibleMemLayoutRunner = new ConversionRunner<TestRgba>(this.Count); |
||||
|
this.permutedRunner = new ConversionRunner<TestArgb>(this.Count); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void CompatibleByRef() |
||||
|
{ |
||||
|
this.compatibleMemLayoutRunner.RunByRefConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CompatibleByVal() |
||||
|
{ |
||||
|
this.compatibleMemLayoutRunner.RunByValConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CompatibleFromBytes() |
||||
|
{ |
||||
|
this.compatibleMemLayoutRunner.RunFromBytesConversion(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Benchmark] |
||||
|
public void PermutedByRef() |
||||
|
{ |
||||
|
this.permutedRunner.RunByRefConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void PermutedByVal() |
||||
|
{ |
||||
|
this.permutedRunner.RunByValConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void PermutedFromBytes() |
||||
|
{ |
||||
|
this.permutedRunner.RunFromBytesConversion(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Results: |
||||
|
* Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | |
||||
|
* ------------------ |------ |----------- |---------- |------- |-------------- | |
||||
|
* CompatibleByRef | 32 | 20.6339 ns | 0.0742 ns | 1.00 | 0.00 | |
||||
|
* CompatibleByVal | 32 | 23.7425 ns | 0.0997 ns | 1.15 | 0.01 | |
||||
|
* CompatibleFromBytes | 32 | 38.7017 ns | 0.1103 ns | 1.88 | 0.01 | |
||||
|
* PermutedByRef | 32 | 39.2892 ns | 0.1366 ns | 1.90 | 0.01 | |
||||
|
* PermutedByVal | 32 | 38.5178 ns | 0.1946 ns | 1.87 | 0.01 | |
||||
|
* PermutedFromBytes | 32 | 38.6683 ns | 0.0801 ns | 1.87 | 0.01 | |
||||
|
* |
||||
|
* !!! Conclusion !!! |
||||
|
* All memory-incompatible (permuted) variants are equivalent with the the "FromBytes" solution. |
||||
|
* In memory compatible cases we should use the optimized Bulk-copying variant anyways, |
||||
|
* so there is no benefit introducing non-bulk API-s other than PackFromBytes() OR PackFromRgba32(). |
||||
|
*/ |
||||
|
} |
||||
@ -0,0 +1,156 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Benchmarks.General |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class PixelConversion_ConvertFromVector4 |
||||
|
{ |
||||
|
interface ITestPixel<T> |
||||
|
where T : struct, ITestPixel<T> |
||||
|
{ |
||||
|
void FromVector4(Vector4 source); |
||||
|
|
||||
|
void FromVector4(ref Vector4 source); |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
struct TestArgb : ITestPixel<TestArgb> |
||||
|
{ |
||||
|
private byte a, r, g, b; |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromVector4(Vector4 p) |
||||
|
{ |
||||
|
this.r = (byte)p.X; |
||||
|
this.g = (byte)p.Y; |
||||
|
this.b = (byte)p.Z; |
||||
|
this.a = (byte)p.W; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void FromVector4(ref Vector4 p) |
||||
|
{ |
||||
|
this.r = (byte)p.X; |
||||
|
this.g = (byte)p.Y; |
||||
|
this.b = (byte)p.Z; |
||||
|
this.a = (byte)p.W; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
struct TestRgbaVector : ITestPixel<TestRgbaVector> |
||||
|
{ |
||||
|
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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct ConversionRunner<T> |
||||
|
where T : struct, ITestPixel<T> |
||||
|
{ |
||||
|
private T[] dest; |
||||
|
|
||||
|
private Vector4[] source; |
||||
|
|
||||
|
public ConversionRunner(int count) |
||||
|
{ |
||||
|
this.dest = new T[count]; |
||||
|
this.source = new Vector4[count]; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void RunByRefConversion() |
||||
|
{ |
||||
|
int count = this.dest.Length; |
||||
|
|
||||
|
ref T destBaseRef = ref this.dest[0]; |
||||
|
ref Vector4 sourceBaseRef = ref this.source[0]; |
||||
|
|
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref destBaseRef, i).FromVector4(ref Unsafe.Add(ref sourceBaseRef, i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void RunByValConversion() |
||||
|
{ |
||||
|
int count = this.dest.Length; |
||||
|
|
||||
|
ref T destBaseRef = ref this.dest[0]; |
||||
|
ref Vector4 sourceBaseRef = ref this.source[0]; |
||||
|
|
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
Unsafe.Add(ref destBaseRef, i).FromVector4(Unsafe.Add(ref sourceBaseRef, i)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ConversionRunner<TestArgb> nonVectorRunner; |
||||
|
|
||||
|
private ConversionRunner<TestRgbaVector> vectorRunner; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.nonVectorRunner = new ConversionRunner<TestArgb>(this.Count); |
||||
|
this.vectorRunner = new ConversionRunner<TestRgbaVector>(this.Count); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void VectorByRef() |
||||
|
{ |
||||
|
this.vectorRunner.RunByRefConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void VectorByVal() |
||||
|
{ |
||||
|
this.vectorRunner.RunByValConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void NonVectorByRef() |
||||
|
{ |
||||
|
this.nonVectorRunner.RunByRefConversion(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void NonVectorByVal() |
||||
|
{ |
||||
|
this.nonVectorRunner.RunByValConversion(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Results: |
||||
|
* Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | |
||||
|
* --------------- |------ |----------- |---------- |------- |-------------- | |
||||
|
* VectorByRef | 32 | 23.6678 ns | 0.1141 ns | 1.00 | 0.00 | |
||||
|
* VectorByVal | 32 | 24.5347 ns | 0.0771 ns | 1.04 | 0.01 | |
||||
|
* NonVectorByRef | 32 | 59.0187 ns | 0.2114 ns | 2.49 | 0.01 | |
||||
|
* NonVectorByVal | 32 | 58.7529 ns | 0.2545 ns | 2.48 | 0.02 | |
||||
|
* |
||||
|
* !!! Conclusion !!! |
||||
|
* We do not need by-ref version of ConvertFromVector4() stuff |
||||
|
*/ |
||||
|
} |
||||
Loading…
Reference in new issue