mirror of https://github.com/SixLabors/ImageSharp
9 changed files with 226 additions and 137 deletions
@ -0,0 +1,63 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Benchmarks.Color.Bulk |
||||
|
{ |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using Color = ImageSharp.Color; |
||||
|
|
||||
|
public abstract class PackFromXyzw<TColor> |
||||
|
where TColor : struct, IPixel<TColor> |
||||
|
{ |
||||
|
private PinnedBuffer<TColor> destination; |
||||
|
|
||||
|
private PinnedBuffer<byte> source; |
||||
|
|
||||
|
[Params(16, 128, 1024)] |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.destination = new PinnedBuffer<TColor>(this.Count); |
||||
|
this.source = new PinnedBuffer<byte>(this.Count * 4); |
||||
|
} |
||||
|
|
||||
|
[Cleanup] |
||||
|
public void Cleanup() |
||||
|
{ |
||||
|
this.destination.Dispose(); |
||||
|
this.source.Dispose(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void PerElement() |
||||
|
{ |
||||
|
byte[] s = this.source.Array; |
||||
|
TColor[] d = this.destination.Array; |
||||
|
|
||||
|
for (int i = 0; i < this.Count; i++) |
||||
|
{ |
||||
|
int i4 = i * 4; |
||||
|
TColor c = default(TColor); |
||||
|
c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]); |
||||
|
d[i] = c; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CommonBulk() |
||||
|
{ |
||||
|
new BulkPixelOperations<TColor>().PackFromXyzwBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void OptimizedBulk() |
||||
|
{ |
||||
|
BulkPixelOperations<TColor>.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class PackFromXyzw_Color : PackFromXyzw<Color> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,129 +0,0 @@ |
|||||
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(BufferPointer<Color> destination, BufferPointer<byte> source, int count); |
|
||||
} |
|
||||
|
|
||||
class UnsafeCopyExecutor : CopyExecutor |
|
||||
{ |
|
||||
[MethodImpl(MethodImplOptions.NoInlining)] |
|
||||
internal override unsafe void VirtualCopy(BufferPointer<Color> destination, BufferPointer<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, 512)] |
|
||||
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 CopyBufferPointerUnsafeInlined() |
|
||||
{ |
|
||||
uint byteCount = (uint)this.area.Width * 4; |
|
||||
|
|
||||
int targetX = this.Width / 4; |
|
||||
int targetY = 0; |
|
||||
|
|
||||
for (int y = 0; y < this.Height; y++) |
|
||||
{ |
|
||||
BufferPointer<byte> source = this.GetAreaRow(y); |
|
||||
BufferPointer<Color> destination = this.GetPixelAccessorRow(targetX, targetY + y); |
|
||||
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Benchmark] |
|
||||
public void CopyBufferPointerUnsafeVirtual() |
|
||||
{ |
|
||||
int targetX = this.Width / 4; |
|
||||
int targetY = 0; |
|
||||
|
|
||||
for (int y = 0; y < this.Height; y++) |
|
||||
{ |
|
||||
BufferPointer<byte> source = this.GetAreaRow(y); |
|
||||
BufferPointer<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 BufferPointer<Color> GetPixelAccessorRow(int x, int y) |
|
||||
{ |
|
||||
return new BufferPointer<ImageSharp.Color>( |
|
||||
this.pixelAccessor.PixelArray, |
|
||||
(void*)this.pixelAccessor.DataPointer, |
|
||||
(y * this.pixelAccessor.Width) + x |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private BufferPointer<byte> GetAreaRow(int y) |
|
||||
{ |
|
||||
return new BufferPointer<byte>(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,61 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Benchmarks.Color.Bulk |
||||
|
{ |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using Color = ImageSharp.Color; |
||||
|
|
||||
|
public abstract class ToXyz<TColor> |
||||
|
where TColor : struct, IPixel<TColor> |
||||
|
{ |
||||
|
private PinnedBuffer<TColor> source; |
||||
|
|
||||
|
private PinnedBuffer<byte> destination; |
||||
|
|
||||
|
[Params(16, 128, 1024)] |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.source = new PinnedBuffer<TColor>(this.Count); |
||||
|
this.destination = new PinnedBuffer<byte>(this.Count * 3); |
||||
|
} |
||||
|
|
||||
|
[Cleanup] |
||||
|
public void Cleanup() |
||||
|
{ |
||||
|
this.source.Dispose(); |
||||
|
this.destination.Dispose(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void PerElement() |
||||
|
{ |
||||
|
TColor[] s = this.source.Array; |
||||
|
byte[] d = this.destination.Array; |
||||
|
|
||||
|
for (int i = 0; i < this.Count; i++) |
||||
|
{ |
||||
|
TColor c = s[i]; |
||||
|
c.ToXyzBytes(d, i * 4); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CommonBulk() |
||||
|
{ |
||||
|
new BulkPixelOperations<TColor>().ToXyzBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void OptimizedBulk() |
||||
|
{ |
||||
|
BulkPixelOperations<TColor>.Instance.ToXyzBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ToXyz_Color : ToXyz<Color> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
|
||||
|
namespace ImageSharp.Benchmarks.Color.Bulk |
||||
|
{ |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
using Color = ImageSharp.Color; |
||||
|
|
||||
|
public abstract class ToXyzw<TColor> |
||||
|
where TColor : struct, IPixel<TColor> |
||||
|
{ |
||||
|
private PinnedBuffer<TColor> source; |
||||
|
|
||||
|
private PinnedBuffer<byte> destination; |
||||
|
|
||||
|
[Params(16, 128, 1024)] |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
[Setup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.source = new PinnedBuffer<TColor>(this.Count); |
||||
|
this.destination = new PinnedBuffer<byte>(this.Count * 4); |
||||
|
} |
||||
|
|
||||
|
[Cleanup] |
||||
|
public void Cleanup() |
||||
|
{ |
||||
|
this.source.Dispose(); |
||||
|
this.destination.Dispose(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public void PerElement() |
||||
|
{ |
||||
|
TColor[] s = this.source.Array; |
||||
|
byte[] d = this.destination.Array; |
||||
|
|
||||
|
for (int i = 0; i < this.Count; i++) |
||||
|
{ |
||||
|
TColor c = s[i]; |
||||
|
c.ToXyzwBytes(d, i * 4); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void CommonBulk() |
||||
|
{ |
||||
|
new BulkPixelOperations<TColor>().ToXyzwBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void OptimizedBulk() |
||||
|
{ |
||||
|
BulkPixelOperations<TColor>.Instance.ToXyzwBytes(this.source, this.destination, this.Count); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ToXyzw_Color : ToXyzw<Color> |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public class ToXyzw_Argb : ToXyzw<Argb> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue