Browse Source

Benchmarks: PixelAccessorVirtualCopy, vectorization

af/merge-core
Anton Firszov 9 years ago
parent
commit
33627ee257
  1. 5
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  2. 129
      tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
  3. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
  4. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
  5. 2
      tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
  6. 2
      tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
  7. 54
      tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs
  8. 54
      tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs
  9. 54
      tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs
  10. 54
      tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs
  11. 54
      tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs
  12. 62
      tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs
  13. 11
      tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
  14. 19
      tests/ImageSharp.Sandbox46/Program.cs
  15. 4
      tests/ImageSharp.Sandbox46/app.config
  16. 1
      tests/ImageSharp.Sandbox46/packages.config

5
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -120,6 +120,11 @@ namespace ImageSharp
/// </summary>
public bool PooledMemory { get; private set; }
/// <summary>
/// Gets the pixel buffer array.
/// </summary>
public TColor[] PixelBuffer => this.pixelBuffer;
/// <summary>
/// Gets the pointer to the pixel buffer.
/// </summary>

129
tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs

@ -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);
}
}
}

2
tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs

@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
var pen = new Pen(Color.HotPink, 10);
var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawBeziers(pen, new[] {
new PointF(10, 500),
new PointF(30, 10),

2
tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs

@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
var pen = new Pen(Color.HotPink, 10);
var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawLines(pen, new[] {
new PointF(10, 10),
new PointF(550, 50),

2
tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs

@ -27,7 +27,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
var pen = new Pen(Color.HotPink, 10);
var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawPolygon(pen, new[] {
new PointF(10, 10),
new PointF(550, 50),

2
tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs

@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks
using (Graphics graphics = Graphics.FromImage(destination))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink);
var brush = new HatchBrush(HatchStyle.BackwardDiagonal, System.Drawing.Color.HotPink);
graphics.FillRectangle(brush, new Rectangle(0,0, 800,800)); // can't find a way to flood fill with a brush
}
using (MemoryStream ms = new MemoryStream())

54
tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs

@ -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);
}
}
}
}

54
tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs

@ -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);
}
}
}
}

54
tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs

@ -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);
}
}
}
}

54
tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs

@ -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);
}
}
}
}

54
tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs

@ -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);
}
}
}
}

62
tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs

@ -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);
}
}
}
}

11
tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj

@ -109,6 +109,10 @@
<HintPath>..\..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net461\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
@ -202,6 +206,9 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ImageSharp.Benchmarks\Color\Bulk\PixelAccessorVirtualCopy.cs">
<Link>Benchmarks\PixelAccessorVirtualCopy.cs</Link>
</Compile>
<Compile Include="..\ImageSharp.Tests\Drawing\PolygonTests.cs">
<Link>Tests\Drawing\PolygonTests.cs</Link>
</Compile>
@ -327,9 +334,7 @@
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="Benchmarks\" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
<Analyzer Include="..\..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />

19
tests/ImageSharp.Sandbox46/Program.cs

@ -8,6 +8,7 @@ namespace ImageSharp.Sandbox46
using System;
using System.Runtime.DesignerServices;
using ImageSharp.Benchmarks.Color.Bulk;
using ImageSharp.Tests;
using Xunit.Abstractions;
@ -36,7 +37,23 @@ namespace ImageSharp.Sandbox46
/// </param>
public static void Main(string[] args)
{
RunDecodeJpegProfilingTests();
//RunDecodeJpegProfilingTests();
TestPixelAccessorCopyFromXyzw();
Console.ReadLine();
}
private static void TestPixelAccessorCopyFromXyzw()
{
PixelAccessorVirtualCopy benchmark = new PixelAccessorVirtualCopy();
benchmark.Width = 64;
benchmark.Setup();
benchmark.CopyRawUnsafeInlined();
benchmark.CopyArrayPointerUnsafe();
benchmark.CopyArrayPointerVirtualUnsafe();
benchmark.CopyArrayPointerVirtualMarshal();
benchmark.Cleanup();
}
private static void RunDecodeJpegProfilingTests()

4
tests/ImageSharp.Sandbox46/app.config

@ -10,6 +10,10 @@
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

1
tests/ImageSharp.Sandbox46/packages.config

@ -29,6 +29,7 @@
<package id="System.Reflection.Primitives" version="4.0.1" targetFramework="net461" />
<package id="System.Resources.ResourceManager" version="4.0.1" targetFramework="net461" />
<package id="System.Runtime" version="4.1.0" targetFramework="net461" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.3.0" targetFramework="net461" />
<package id="System.Runtime.Extensions" version="4.1.0" targetFramework="net461" />
<package id="System.Runtime.Handles" version="4.0.1" targetFramework="net461" />
<package id="System.Runtime.InteropServices" version="4.1.0" targetFramework="net461" />

Loading…
Cancel
Save