From 15a187a8b3b45081825a7a1fc59d310602ce31e7 Mon Sep 17 00:00:00 2001 From: Olivier Coanet Date: Wed, 8 Jan 2020 09:35:15 +0100 Subject: [PATCH] Add benchmarks --- .../BatchEventProcessorBenchmarks.cs | 140 +++++++++++------ .../ObjectArrayBenchmarks.cs | 146 +----------------- src/Disruptor.Benchmarks/Program.cs | 17 +- .../RingBufferBenchmarks.cs | 41 +++-- .../SequencerDispatcherBenchmarks.cs | 30 ++++ .../StructProxyBenchmarks.cs | 56 +++++++ 6 files changed, 222 insertions(+), 208 deletions(-) create mode 100644 src/Disruptor.Benchmarks/SequencerDispatcherBenchmarks.cs create mode 100644 src/Disruptor.Benchmarks/StructProxyBenchmarks.cs diff --git a/src/Disruptor.Benchmarks/BatchEventProcessorBenchmarks.cs b/src/Disruptor.Benchmarks/BatchEventProcessorBenchmarks.cs index 45ff078d..881c29a3 100644 --- a/src/Disruptor.Benchmarks/BatchEventProcessorBenchmarks.cs +++ b/src/Disruptor.Benchmarks/BatchEventProcessorBenchmarks.cs @@ -1,78 +1,130 @@ using System; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; +using Disruptor.Internal; namespace Disruptor.Benchmarks { public class BatchEventProcessorBenchmarks { - private readonly Sequence _sequence = new Sequence(); - private readonly RingBuffer _ringBuffer; - private readonly TestEventHandler _eventHandler; - private readonly ISequenceBarrier _sequenceBarrier; + private readonly IRunner _runner; public BatchEventProcessorBenchmarks() { - _ringBuffer = new RingBuffer(() => new TestEvent(), new SingleProducerSequencer(4096, new SpinWaitWaitStrategy())); - _eventHandler = new TestEventHandler(); - _sequenceBarrier = _ringBuffer.NewBarrier(); + var ringBuffer = new RingBuffer(() => new XEvent(), new SingleProducerSequencer(4096, new SpinWaitWaitStrategy())); + var eventHandler = new XEventHandler(); + var sequenceBarrier = ringBuffer.NewBarrier(); - _ringBuffer.PublishEvent().Dispose(); - } + ringBuffer.PublishEvent().Dispose(); + + var dataProviderProxy = StructProxy.CreateProxyInstance>(ringBuffer); + var sequenceBarrierProxy = StructProxy.CreateProxyInstance(sequenceBarrier); + var eventHandlerProxy = StructProxy.CreateProxyInstance>(eventHandler); + var batchStartAwareProxy = new NoopBatchStartAware(); - public volatile int Running; + var runnerType = typeof(Runner<,,,,>).MakeGenericType(typeof(XEvent), dataProviderProxy.GetType(), sequenceBarrierProxy.GetType(), eventHandlerProxy.GetType(), batchStartAwareProxy.GetType()); + _runner = (IRunner)Activator.CreateInstance(runnerType, dataProviderProxy, sequenceBarrierProxy, eventHandlerProxy, batchStartAwareProxy); + } [Benchmark] public long ProcessEvent() { - var nextSequence = 0L; - try - { - var availableSequence = _sequenceBarrier.WaitFor(nextSequence); + return _runner.ProcessEvent(); + } - while (nextSequence <= availableSequence) - { - var evt = _ringBuffer[nextSequence]; - _eventHandler.OnEvent(evt, nextSequence, nextSequence == availableSequence); - nextSequence++; - } + public class XEvent + { + public long Data { get; set; } + } - _sequence.SetValue(availableSequence); - } - catch (TimeoutException) - { - NotifyTimeout(_sequence.Value); - } - catch (AlertException) - { - if (Running != 2) - { - return nextSequence; - } - } - catch (Exception) + public class XEventHandler : IEventHandler + { + public long Sum; + + public void OnEvent(XEvent data, long sequence, bool endOfBatch) { - _sequence.SetValue(nextSequence); - nextSequence++; + Sum += data.Data; } - - return nextSequence; } - [MethodImpl(MethodImplOptions.NoInlining)] - private void NotifyTimeout(long sequenceValue) + private struct NoopBatchStartAware : IBatchStartAware { + public void OnBatchStart(long batchSize) + { + } } - public class TestEvent + public interface IRunner { - public long Data { get; set; } + long ProcessEvent(); } - public class TestEventHandler : IEventHandler + public class Runner : IRunner + where T : class + where TDataProvider : IDataProvider + where TSequenceBarrier : ISequenceBarrier + where TEventHandler : IEventHandler + where TBatchStartAware : IBatchStartAware { - public void OnEvent(TestEvent data, long sequence, bool endOfBatch) + private readonly Sequence _sequence = new Sequence(); + private IExceptionHandler _exceptionHandler = new FatalExceptionHandler(); + + private TDataProvider _dataProvider; + private TSequenceBarrier _sequenceBarrier; + private TEventHandler _eventHandler; + private TBatchStartAware _batchStartAware; + + public volatile int Running; + + public Runner(TDataProvider dataProvider, TSequenceBarrier sequenceBarrier, TEventHandler eventHandler, TBatchStartAware batchStartAware) + { + _dataProvider = dataProvider; + _sequenceBarrier = sequenceBarrier; + _eventHandler = eventHandler; + _batchStartAware = batchStartAware; + } + + public long ProcessEvent() + { + T evt = null; + var nextSequence = _sequence.Value + 1L; + + try + { + var availableSequence = _sequenceBarrier.WaitFor(nextSequence); + + _batchStartAware.OnBatchStart(availableSequence - nextSequence + 1); + + while (nextSequence <= availableSequence) + { + evt = _dataProvider[nextSequence]; + _eventHandler.OnEvent(evt, nextSequence, nextSequence == availableSequence); + nextSequence++; + } + + //_sequence.SetValue(availableSequence); + } + catch (TimeoutException) + { + NotifyTimeout(_sequence.Value); + } + catch (AlertException) + { + } + catch (Exception ex) + { + _exceptionHandler.HandleEventException(ex, nextSequence, evt); + _sequence.SetValue(nextSequence); + nextSequence++; + } + + return nextSequence; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void NotifyTimeout(long sequence) { + Console.WriteLine(sequence); } } } diff --git a/src/Disruptor.Benchmarks/ObjectArrayBenchmarks.cs b/src/Disruptor.Benchmarks/ObjectArrayBenchmarks.cs index 42e2bcc9..3d2575e8 100644 --- a/src/Disruptor.Benchmarks/ObjectArrayBenchmarks.cs +++ b/src/Disruptor.Benchmarks/ObjectArrayBenchmarks.cs @@ -1,165 +1,33 @@ -using System; -using System.Reflection.Emit; +using System.Linq; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using InlineIL; -using static InlineIL.IL.Emit; namespace Disruptor.Benchmarks { public class ObjectArrayBenchmarks { - private static readonly int _offsetToArrayData = ElemOffset(new object[1]); - private readonly object[] _array; + private readonly Event[] _array; public ObjectArrayBenchmarks() { - _array = new object[1024]; - for (int i = 0; i < _array.Length; i++) - { - _array[i] = new Event { Value = i }; - } - - var item = _array[42]; - - if (!ReferenceEquals(ReadILImpl(42), item)) - throw new InvalidOperationException(); - - if (!ReferenceEquals(ReadILImpl2(42), item)) - throw new InvalidOperationException(); + _array = Enumerable.Range(0, 1024) + .Select(i => new Event { Value = i }) + .ToArray(); } public int Index = 371; [Benchmark(Baseline = true)] - [MethodImpl(MethodImplOptions.NoInlining)] public int ReadOne() { - return ReadImpl(Index).Value; - } - - //[Benchmark(OperationsPerInvoke = 1024)] - [MethodImpl(MethodImplOptions.NoInlining)] - public int ReadMany() - { - var sum = 0; - for (int i = 0; i < 1024; i++) - { - sum += ReadImpl(i).Value; - } - - return sum; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Event ReadImplPublic(int index) - { - return ReadImpl(index); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private T ReadImpl(int index) - { - return (T)_array[index]; - } - - [Benchmark] - [MethodImpl(MethodImplOptions.NoInlining)] - public int ReadOneUnsafe() - { - return ReadUnsafeImpl(Index).Value; - } - - //[Benchmark(OperationsPerInvoke = 1024)] - [MethodImpl(MethodImplOptions.NoInlining)] - public int ReadManyUnsafe() - { - var sum = 0; - for (int i = 0; i < 1024; i++) - { - sum += ReadUnsafeImpl(i).Value; - } - - return sum; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public Event ReadUnsafeImplPublic(int index) - { - return ReadUnsafeImpl(index); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private T ReadUnsafeImpl(int index) - { - ref var firstItem = ref Unsafe.As(ref _array[0]); - return Unsafe.Add(ref firstItem, index); + return _array[Index].Value; } [Benchmark] [MethodImpl(MethodImplOptions.NoInlining)] public int ReadOneIL() { - return ReadILImpl(Index).Value; - } - - [Benchmark] - [MethodImpl(MethodImplOptions.NoInlining)] - public int ReadOneIL2() - { - return ReadILImpl2(Index).Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T ReadILImpl(int index) - { - IL.DeclareLocals( - false, - typeof(byte).MakeByRefType() - ); - - Ldarg_0(); - Ldfld(new FieldRef(typeof(ObjectArrayBenchmarks), nameof(_array))); - Stloc_0(); - Ldloc_0(); - - Ldarg(nameof(index)); - Sizeof(typeof(object)); - Mul(); - - Ldsfld(new FieldRef(typeof(ObjectArrayBenchmarks), nameof(_offsetToArrayData))); - Add(); - - Add(); - - Ldobj(typeof(T)); - - return IL.Return(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T ReadILImpl2(int index) - { - Ldarg_0(); - Ldfld(new FieldRef(typeof(ObjectArrayBenchmarks), nameof(_array))); - - Ldarg(nameof(index)); - Readonly(); // Trigger this codepath in the JIT: https://github.com/dotnet/coreclr/blob/bc28740cd5f0533655f347fc315f6a28836a7efe/src/jit/importer.cpp#L11141-L11147 - Ldelema(typeof(T)); - Ldind_Ref(); - - return IL.Return(); - } - - private static int ElemOffset(T[] arr) - { - Ldarg(nameof(arr)); - Ldc_I4_0(); - Ldelema(typeof(T)); - Ldarg(nameof(arr)); - Sub(); - - return IL.Return(); + return Util.Read(_array, Index).Value; } public class Event diff --git a/src/Disruptor.Benchmarks/Program.cs b/src/Disruptor.Benchmarks/Program.cs index 08987614..a9acd83a 100644 --- a/src/Disruptor.Benchmarks/Program.cs +++ b/src/Disruptor.Benchmarks/Program.cs @@ -29,7 +29,7 @@ public static void Main() BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(); - //Console.ReadLine(); + Console.ReadLine(); } private static void RunMultiProducerSequencerBenchmarks() @@ -72,20 +72,5 @@ private static void RunInt32ArrayBenchmarks() bench.Read(); bench.ReadFixed(); } - - private static void RunObjectArrayBenchmarks() - { - var bench = new ObjectArrayBenchmarks(); - - bench.ReadImplPublic(371); - bench.ReadUnsafeImplPublic(371); - - Console.WriteLine("YYY"); - Console.ReadLine(); - Console.WriteLine("ZZZ"); - - bench.ReadImplPublic(371); - bench.ReadUnsafeImplPublic(371); - } } } diff --git a/src/Disruptor.Benchmarks/RingBufferBenchmarks.cs b/src/Disruptor.Benchmarks/RingBufferBenchmarks.cs index fa78abc3..3e4f35d8 100644 --- a/src/Disruptor.Benchmarks/RingBufferBenchmarks.cs +++ b/src/Disruptor.Benchmarks/RingBufferBenchmarks.cs @@ -2,17 +2,40 @@ namespace Disruptor.Benchmarks { - //[DisassemblyDiagnoser] public class RingBufferBenchmarks { private readonly RingBuffer _ringBuffer; + private ISequenceBarrier _sequenceBarrier; public RingBufferBenchmarks() { _ringBuffer = new RingBuffer(() => new Event(), new SingleProducerSequencer(4096, new BusySpinWaitStrategy())); + _sequenceBarrier = _ringBuffer.NewBarrier(); } - [Benchmark(Baseline = true)] + [Benchmark] + public void PublishAndWait() + { + var sequence = _ringBuffer.Next(); + try + { + var data = _ringBuffer[sequence]; + data.L1 = 1; + data.L2 = 2; + data.L3 = 3; + data.L4 = 4; + data.L5 = 5; + data.L6 = 6; + } + finally + { + _ringBuffer.Publish(sequence); + } + + _sequenceBarrier.WaitFor(sequence); + } + + // [Benchmark(Baseline = true)] public void PublishClassic() { var sequence = _ringBuffer.Next(); @@ -32,7 +55,7 @@ public void PublishClassic() } } - [Benchmark] + // [Benchmark] public void PublishScope() { using (var scope = _ringBuffer.PublishEvent()) @@ -47,7 +70,7 @@ public void PublishScope() } } - [Benchmark] + // [Benchmark] public void TryPublishClassic() { if (!_ringBuffer.TryNext(out var sequence)) @@ -69,7 +92,7 @@ public void TryPublishClassic() } } - [Benchmark] + // [Benchmark] public void TryPublishScope() { using (var scope = _ringBuffer.TryPublishEvent()) @@ -87,7 +110,7 @@ public void TryPublishScope() } } - [Benchmark] + // [Benchmark] public void PublishManyClassic() { var sequence = _ringBuffer.Next(2); @@ -107,7 +130,7 @@ public void PublishManyClassic() } } - [Benchmark] + // [Benchmark] public void PublishManyScope() { using (var scope = _ringBuffer.PublishEvents(2)) @@ -122,7 +145,7 @@ public void PublishManyScope() } } - [Benchmark] + // [Benchmark] public void TryPublishManyClassic() { if (!_ringBuffer.TryNext(1, out var s)) @@ -144,7 +167,7 @@ public void TryPublishManyClassic() } } - [Benchmark] + // [Benchmark] public void TryPublishManyScope() { using (var scope = _ringBuffer.TryPublishEvents(2)) diff --git a/src/Disruptor.Benchmarks/SequencerDispatcherBenchmarks.cs b/src/Disruptor.Benchmarks/SequencerDispatcherBenchmarks.cs new file mode 100644 index 00000000..6b0cbe9e --- /dev/null +++ b/src/Disruptor.Benchmarks/SequencerDispatcherBenchmarks.cs @@ -0,0 +1,30 @@ +using BenchmarkDotNet.Attributes; + +namespace Disruptor.Benchmarks +{ + public class SequencerDispatcherBenchmarks + { + private readonly ISequencer _sequencer; + private readonly SequencerDispatcher _dispatcher; + + public SequencerDispatcherBenchmarks() + { + _sequencer = new SingleProducerSequencer(1024); + _dispatcher = new SequencerDispatcher(_sequencer); + } + + [Benchmark(Baseline = true)] + public void UseInterface() + { + var sequence = _sequencer.Next(); + _sequencer.Publish(sequence); + } + + [Benchmark] + public void UseDispatcher() + { + var sequence = _dispatcher.Next(); + _dispatcher.Publish(sequence); + } + } +} diff --git a/src/Disruptor.Benchmarks/StructProxyBenchmarks.cs b/src/Disruptor.Benchmarks/StructProxyBenchmarks.cs new file mode 100644 index 00000000..105bc323 --- /dev/null +++ b/src/Disruptor.Benchmarks/StructProxyBenchmarks.cs @@ -0,0 +1,56 @@ +using System; +using BenchmarkDotNet.Attributes; +using Disruptor.Internal; + +namespace Disruptor.Benchmarks +{ + public class StructProxyBenchmarks + { + private IRunner _defaultRunner; + private IRunner _generatedRunner; + + public StructProxyBenchmarks() + { + _defaultRunner = new Runner>(new Impl()); + + var impl = StructProxy.CreateProxyInstance>(new Impl()); + _generatedRunner = (IRunner)Activator.CreateInstance(typeof(Runner<>).MakeGenericType(impl.GetType()), impl); + } + + [Benchmark(Baseline = true)] + public int GetDefault() => _defaultRunner.GetData(100).Value; + + [Benchmark] + public int GetGenerated() => _generatedRunner.GetData(100).Value; + + public interface IRunner + { + Event GetData(long sequence); + } + + public class Runner : IRunner + where TDataProvider : IDataProvider + { + private readonly TDataProvider _dataProvider; + + public Runner(TDataProvider dataProvider) + { + _dataProvider = dataProvider; + } + + public Event GetData(long sequence) => _dataProvider[sequence]; + } + + public class Event + { + public int Value; + } + + public class Impl : IDataProvider + { + public readonly Event Data = new Event { Value = 42 }; + + public Event this[long sequence] => Data; + } + } +}