From 57cc5c5f300ba5a329bd4e82a96ab4859f5df7d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 13 Nov 2016 15:28:22 +0100 Subject: [PATCH] benchmarking benchmarking benchmarking --- .../Formats/Jpg/Components/Block8x8.cs | 79 +++++- .../Formats/Jpg/Block8x8Tests.cs | 259 ++++++++---------- 2 files changed, 185 insertions(+), 153 deletions(-) diff --git a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs index 0169dce22..123b2efe5 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs @@ -1,6 +1,8 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + // ReSharper disable InconsistentNaming namespace ImageSharp.Formats @@ -35,30 +37,47 @@ namespace ImageSharp.Formats public const int VectorCount = 16; public const int ScalarCount = VectorCount * 4; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void LoadFrom(Span source) { fixed (Vector4* ptr = &V0L) { - float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) - { - fp[i] = source[i]; - } + Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount); + //float* fp = (float*)ptr; + //for (int i = 0; i < ScalarCount; i++) + //{ + // fp[i] = source[i]; + //} } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyTo(Span dest) { fixed (Vector4* ptr = &V0L) { - float* fp = (float*)ptr; - for (int i = 0; i < ScalarCount; i++) - { - dest[i] = fp[i]; - } + Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount); + //float* fp = (float*)ptr; + //for (int i = 0; i < ScalarCount; i++) + //{ + // dest[i] = fp[i]; + //} } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void LoadFrom(Block8x8* blockPtr, Span source) + { + Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void CopyTo(Block8x8* blockPtr, Span dest) + { + Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount); + } + + internal unsafe void LoadFrom(Span source) { fixed (Vector4* ptr = &V0L) @@ -104,7 +123,7 @@ namespace ImageSharp.Formats } /// - /// Used as a reference implementation for benchmarking + /// Reference implementation we can benchmark against /// internal unsafe void TransposeInto_PinningImpl(ref Block8x8 destination) { @@ -145,9 +164,7 @@ namespace ImageSharp.Formats } } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyAllInplace(Vector4 s) { @@ -411,5 +428,41 @@ namespace ImageSharp.Formats source.IDCTInto(ref dest, ref temp); dest.CopyTo(block.Data); } + + public unsafe float this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + fixed (Block8x8* p = &this) + { + float* fp = (float*) p; + return fp[idx]; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + fixed (Block8x8* p = &this) + { + float* fp = (float*)p; + fp[idx] = value; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe float GetScalarAt(Block8x8* blockPtr, int idx) + { + float* fp = (float*) blockPtr; + return fp[idx]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void SetScalarAt(Block8x8* blockPtr, int idx, float value) + { + float* fp = (float*)blockPtr; + fp[idx] = value; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs index 1fa1e1827..ae4c32c48 100644 --- a/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs +++ b/tests/ImageSharp.Tests46/Formats/Jpg/Block8x8Tests.cs @@ -1,7 +1,11 @@ -using System; +// Uncomment this to turn unit tests into benchmarks: +#define BENCHMARKING + +using System; using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using ImageSharp.Formats; using Xunit; @@ -12,17 +16,84 @@ namespace ImageSharp.Tests.Formats.Jpg // ReSharper disable once InconsistentNaming public class Block8x8Tests : UtilityTestClassBase { +#if BENCHMARKING + public const int Times = 1000000; +#else + public const int Times = 1; +#endif + public Block8x8Tests(ITestOutputHelper output) : base(output) { } - - [Theory] - [InlineData(1)] - [InlineData(1000000)] - public void Load_Store_FloatArray(int times) + + [Fact] + public void Indexer() + { + float sum = 0; + Measure(Times, () => + { + Block8x8 block = new Block8x8(); + + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + block[i] = i; + } + sum = 0; + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + sum += block[i]; + } + }); + Assert.Equal(sum, 64f*63f*0.5f); + } + + [Fact] + public unsafe void Indexer_GetScalarAt_SetScalarAt() + { + float sum = 0; + Measure(Times, () => + { + Block8x8 block = new Block8x8(); + + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + Block8x8.SetScalarAt(&block, i, i); + } + sum = 0; + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + sum += Block8x8.GetScalarAt(&block, i); + } + }); + Assert.Equal(sum, 64f * 63f * 0.5f); + } + + [Fact] + public void Indexer_ReferenceBenchmarkWithArray() { + float sum = 0; + float[] block = new float[64]; + Measure(Times, () => + { + //Block8x8 block = new Block8x8(); + + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + block[i] = i; + } + sum = 0; + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + sum += block[i]; + } + }); + Assert.Equal(sum, 64f * 63f * 0.5f); + } + [Fact] + public void Load_Store_FloatArray() + { float[] data = new float[Block8x8.ScalarCount]; float[] mirror = new float[Block8x8.ScalarCount]; @@ -30,24 +101,41 @@ namespace ImageSharp.Tests.Formats.Jpg { data[i] = i; } - Measure(times, () => + Measure(Times, () => { - Block8x8 v = new Block8x8(); - v.LoadFrom(data); - v.CopyTo(mirror); + Block8x8 b = new Block8x8(); + b.LoadFrom(data); + b.CopyTo(mirror); }); Assert.Equal(data, mirror); - PrintLinearData((Span)mirror); + //PrintLinearData((Span)mirror); } - [Theory] - [InlineData(1)] - [InlineData(1000000)] - public void Load_Store_IntArray(int times) + [Fact] + public unsafe void Load_Store_FloatArray_Ptr() { + float[] data = new float[Block8x8.ScalarCount]; + float[] mirror = new float[Block8x8.ScalarCount]; + for (int i = 0; i < Block8x8.ScalarCount; i++) + { + data[i] = i; + } + Measure(Times, () => + { + Block8x8 b = new Block8x8(); + Block8x8.LoadFrom(&b, data); + Block8x8.CopyTo(&b, mirror); + }); + Assert.Equal(data, mirror); + //PrintLinearData((Span)mirror); + } + + [Fact] + public void Load_Store_IntArray() + { int[] data = new int[Block8x8.ScalarCount]; int[] mirror = new int[Block8x8.ScalarCount]; @@ -55,7 +143,7 @@ namespace ImageSharp.Tests.Formats.Jpg { data[i] = i; } - Measure(times, () => + Measure(Times, () => { Block8x8 v = new Block8x8(); v.LoadFrom(data); @@ -63,7 +151,7 @@ namespace ImageSharp.Tests.Formats.Jpg }); Assert.Equal(data, mirror); - PrintLinearData((Span)mirror); + //PrintLinearData((Span)mirror); } [Fact] @@ -172,40 +260,37 @@ namespace ImageSharp.Tests.Formats.Jpg public Block8x8 Buffer; } - [Theory] - [InlineData(1)] - [InlineData(10000000)] - public void TranposeInto_Benchmark(int times) + [Fact] + public void TranposeInto_Benchmark() { BufferHolder source = new BufferHolder(); source.Buffer.LoadFrom(Create8x8FloatData()); BufferHolder dest = new BufferHolder(); - Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {times} ..."); + Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); Stopwatch sw = Stopwatch.StartNew(); - for (int i = 0; i < times; i++) + for (int i = 0; i < Times; i++) { source.Buffer.TransposeInto(ref dest.Buffer); } sw.Stop(); Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); + } - [Theory] - [InlineData(1)] - [InlineData(10000000)] - public void TranposeInto_PinningImpl_Benchmark(int times) + [Fact] + public void TranposeInto_PinningImpl_Benchmark() { BufferHolder source = new BufferHolder(); source.Buffer.LoadFrom(Create8x8FloatData()); BufferHolder dest = new BufferHolder(); - Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {times} ..."); + Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); Stopwatch sw = Stopwatch.StartNew(); - for (int i = 0; i < times; i++) + for (int i = 0; i < Times; i++) { source.Buffer.TransposeInto_PinningImpl(ref dest.Buffer); } @@ -214,10 +299,8 @@ namespace ImageSharp.Tests.Formats.Jpg Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); } - [Theory] - [InlineData(1)] - [InlineData(10000000)] - public unsafe void TransposeInto_WithPointers_Benchmark(int times) + [Fact] + public unsafe void TransposeInto_WithPointers_Benchmark() { BufferHolder source = new BufferHolder(); source.Buffer.LoadFrom(Create8x8FloatData()); @@ -227,10 +310,10 @@ namespace ImageSharp.Tests.Formats.Jpg { fixed (Block8x8* dPtr = &dest.Buffer) { - Output.WriteLine($"TransposeInto_WithPointers_Benchmark X {times} ..."); + Output.WriteLine($"TransposeInto_WithPointers_Benchmark X {Times} ..."); Stopwatch sw = Stopwatch.StartNew(); - for (int i = 0; i < times; i++) + for (int i = 0; i < Times; i++) { Block8x8.TransposeInto(sPtr, dPtr); } @@ -318,110 +401,6 @@ namespace ImageSharp.Tests.Formats.Jpg Print8x8Data(actualDestArray); Assert.Equal(expectedDestArray, actualDestArray); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void TransposeMatrix(ref Matrix4x4 s, ref Matrix4x4 d) - { - d.M11 = s.M11; - d.M12 = s.M21; - d.M13 = s.M31; - d.M14 = s.M41; - d.M21 = s.M12; - d.M22 = s.M22; - d.M23 = s.M32; - d.M24 = s.M42; - d.M31 = s.M13; - d.M32 = s.M23; - d.M33 = s.M33; - d.M34 = s.M43; - d.M41 = s.M14; - d.M42 = s.M24; - d.M43 = s.M34; - d.M44 = s.M44; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void TransposeMatrixPtr(float* s, float* d) - { - for (int i = 0; i < 4; i++) - { - int i4 = i*4; - for (int j = 0; j < 4; j++) - { - d[j*4 + i] = s[i4 + j]; - } - } - } - - - - - [Theory] - [InlineData(50000000)] - public void TransposeMatrix_Custom(int times) - { - Matrix4x4 s = new Matrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - Matrix4x4 d = new Matrix4x4(); - - Output.WriteLine($"TransposeMatrix_System X {times} ..."); - Stopwatch sw = Stopwatch.StartNew(); - - for (int i = 0; i < times; i++) - { - TransposeMatrix(ref s, ref d); - } - - sw.Stop(); - Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); - - Output.WriteLine(d.ToString()); - } - - - - [Theory] - [InlineData(50000000)] - public unsafe void TransposeMatrix_System(int times) - { - Matrix4x4 s = new Matrix4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); - Matrix4x4 d = new Matrix4x4(); - - Output.WriteLine($"TransposeMatrix_System X {times} ..."); - Stopwatch sw = Stopwatch.StartNew(); - - for (int i = 0; i < times; i++) - { - d = Matrix4x4.Transpose(s); - } - - sw.Stop(); - Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); - - Output.WriteLine(d.ToString()); - } - - [Theory] - [InlineData(50000000)] - public unsafe void TransposeMatrix_Ptr(int times) - { - Matrix4x4 s = new Matrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - Matrix4x4 d = new Matrix4x4(); - - float* sPtr = (float*) &s; - float* dPtr = (float*) &d; - - Output.WriteLine($"TransposeMatrix_System X {times} ..."); - Stopwatch sw = Stopwatch.StartNew(); - - for (int i = 0; i < times; i++) - { - TransposeMatrixPtr(sPtr,dPtr); - } - - sw.Stop(); - Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); - - Output.WriteLine(d.ToString()); - } + } } \ No newline at end of file