mirror of https://github.com/SixLabors/ImageSharp
16 changed files with 501 additions and 8 deletions
@ -0,0 +1,129 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ImageSharp.Benchmarks.Color.Bulk |
||||
|
{ |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using Color = ImageSharp.Color; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods
|
||||
|
/// </summary>
|
||||
|
public unsafe class PixelAccessorVirtualCopy |
||||
|
{ |
||||
|
abstract class CopyExecutor |
||||
|
{ |
||||
|
internal abstract void VirtualCopy(ArrayPointer<Color> destination, ArrayPointer<byte> source, int count); |
||||
|
} |
||||
|
|
||||
|
class UnsafeCopyExecutor : CopyExecutor |
||||
|
{ |
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
internal override unsafe void VirtualCopy(ArrayPointer<Color> destination, ArrayPointer<byte> source, int count) |
||||
|
{ |
||||
|
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private PixelAccessor<Color> pixelAccessor; |
||||
|
|
||||
|
private PixelArea<Color> area; |
||||
|
|
||||
|
private CopyExecutor executor; |
||||
|
|
||||
|
[Params(64, 256)] |
||||
|
public int Width { get; set; } |
||||
|
|
||||
|
public int Height { get; set; } = 256; |
||||
|
|
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.pixelAccessor = new PixelAccessor<ImageSharp.Color>(this.Width, this.Height); |
||||
|
this.area = new PixelArea<Color>(this.Width / 2, this.Height, ComponentOrder.Xyzw); |
||||
|
this.executor = new UnsafeCopyExecutor(); |
||||
|
} |
||||
|
|
||||
|
[Cleanup] |
||||
|
public void Cleanup() |
||||
|
{ |
||||
|
this.pixelAccessor.Dispose(); |
||||
|
this.area.Dispose(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void CopyRawUnsafeInlined() |
||||
|
{ |
||||
|
uint byteCount = (uint)this.area.Width * 4; |
||||
|
|
||||
|
int targetX = this.Width / 4; |
||||
|
int targetY = 0; |
||||
|
|
||||
|
for (int y = 0; y < this.Height; y++) |
||||
|
{ |
||||
|
byte* source = this.area.PixelBase + (y * this.area.RowStride); |
||||
|
byte* destination = this.GetRowPointer(targetX, targetY + y); |
||||
|
|
||||
|
Unsafe.CopyBlock(destination, source, byteCount); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CopyArrayPointerUnsafeInlined() |
||||
|
{ |
||||
|
uint byteCount = (uint)this.area.Width * 4; |
||||
|
|
||||
|
int targetX = this.Width / 4; |
||||
|
int targetY = 0; |
||||
|
|
||||
|
for (int y = 0; y < this.Height; y++) |
||||
|
{ |
||||
|
ArrayPointer<byte> source = this.GetAreaRow(y); |
||||
|
ArrayPointer<Color> destination = this.GetPixelAccessorRow(targetX, targetY + y); |
||||
|
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CopyArrayPointerUnsafeVirtual() |
||||
|
{ |
||||
|
int targetX = this.Width / 4; |
||||
|
int targetY = 0; |
||||
|
|
||||
|
for (int y = 0; y < this.Height; y++) |
||||
|
{ |
||||
|
ArrayPointer<byte> source = this.GetAreaRow(y); |
||||
|
ArrayPointer<Color> destination = this.GetPixelAccessorRow(targetX, targetY + y); |
||||
|
this.executor.VirtualCopy(destination, source, this.area.Width); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private byte* GetRowPointer(int x, int y) |
||||
|
{ |
||||
|
return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf<Color>()); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private ArrayPointer<Color> GetPixelAccessorRow(int x, int y) |
||||
|
{ |
||||
|
return new ArrayPointer<ImageSharp.Color>( |
||||
|
this.pixelAccessor.PixelBuffer, |
||||
|
(void*)this.pixelAccessor.DataPointer, |
||||
|
(y * this.pixelAccessor.Width) + x |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
private ArrayPointer<byte> GetAreaRow(int y) |
||||
|
{ |
||||
|
return new ArrayPointer<byte>(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class BitwiseOrUInt32 |
||||
|
{ |
||||
|
private uint[] input; |
||||
|
|
||||
|
private uint[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
private uint testValue; |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new uint[this.InputSize]; |
||||
|
this.result = new uint[this.InputSize]; |
||||
|
this.testValue = 42; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint) i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
uint v = this.testValue; |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
this.result[i] = this.input[i] | v; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
Vector<uint> v = new Vector<uint>(this.testValue); |
||||
|
|
||||
|
for (int i = 0; i < this.input.Length; i+=Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<uint> a = new Vector<uint>(this.input, i); |
||||
|
a = Vector.BitwiseOr(a, v); |
||||
|
a.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class DivFloat |
||||
|
{ |
||||
|
private float[] input; |
||||
|
|
||||
|
private float[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
private float testValue; |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new float[this.InputSize]; |
||||
|
this.result = new float[this.InputSize]; |
||||
|
this.testValue = 42; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint)i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
float v = this.testValue; |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
this.result[i] = this.input[i] / v; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
Vector<float> v = new Vector<float>(this.testValue); |
||||
|
|
||||
|
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<float> a = new Vector<float>(this.input, i); |
||||
|
a = a / v; |
||||
|
a.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class DivUInt32 |
||||
|
{ |
||||
|
private uint[] input; |
||||
|
|
||||
|
private uint[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
private uint testValue; |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new uint[this.InputSize]; |
||||
|
this.result = new uint[this.InputSize]; |
||||
|
this.testValue = 42; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint)i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
uint v = this.testValue; |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
this.result[i] = this.input[i] / v; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
Vector<uint> v = new Vector<uint>(this.testValue); |
||||
|
|
||||
|
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<uint> a = new Vector<uint>(this.input, i); |
||||
|
a = a / v; |
||||
|
a.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class MulFloat |
||||
|
{ |
||||
|
private float[] input; |
||||
|
|
||||
|
private float[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
private float testValue; |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new float[this.InputSize]; |
||||
|
this.result = new float[this.InputSize]; |
||||
|
this.testValue = 42; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint)i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
float v = this.testValue; |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
this.result[i] = this.input[i] * v; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
Vector<float> v = new Vector<float>(this.testValue); |
||||
|
|
||||
|
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<float> a = new Vector<float>(this.input, i); |
||||
|
a = a * v; |
||||
|
a.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class MulUInt32 |
||||
|
{ |
||||
|
private uint[] input; |
||||
|
|
||||
|
private uint[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
private uint testValue; |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new uint[this.InputSize]; |
||||
|
this.result = new uint[this.InputSize]; |
||||
|
this.testValue = 42; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint)i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
uint v = this.testValue; |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
this.result[i] = this.input[i] * v; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
Vector<uint> v = new Vector<uint>(this.testValue); |
||||
|
|
||||
|
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<uint> a = new Vector<uint>(this.input, i); |
||||
|
a = a * v; |
||||
|
a.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
namespace ImageSharp.Benchmarks.General.Vectorization |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
public class ReinterpretUInt32AsFloat |
||||
|
{ |
||||
|
private uint[] input; |
||||
|
|
||||
|
private float[] result; |
||||
|
|
||||
|
[Params(32)] |
||||
|
public int InputSize { get; set; } |
||||
|
|
||||
|
[StructLayout(LayoutKind.Explicit)] |
||||
|
struct UIntFloatUnion |
||||
|
{ |
||||
|
[FieldOffset(0)] |
||||
|
public float f; |
||||
|
|
||||
|
[FieldOffset(0)] |
||||
|
public uint i; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.input = new uint[this.InputSize]; |
||||
|
this.result = new float[this.InputSize]; |
||||
|
|
||||
|
for (int i = 0; i < this.InputSize; i++) |
||||
|
{ |
||||
|
this.input[i] = (uint)i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void Standard() |
||||
|
{ |
||||
|
UIntFloatUnion u = default(UIntFloatUnion); |
||||
|
for (int i = 0; i < this.input.Length; i++) |
||||
|
{ |
||||
|
u.i = this.input[i]; |
||||
|
this.result[i] = u.f; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Simd() |
||||
|
{ |
||||
|
for (int i = 0; i < this.input.Length; i += Vector<uint>.Count) |
||||
|
{ |
||||
|
Vector<uint> a = new Vector<uint>(this.input, i); |
||||
|
Vector<float> b = Vector.AsVectorSingle(a); |
||||
|
b.CopyTo(this.result, i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue