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