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