diff --git a/csharp/sbe-benchmarks/Bench/SBE/BenchmarkState.cs b/csharp/sbe-benchmarks/Bench/SBE/BenchmarkState.cs deleted file mode 100644 index e82c8ac27d..0000000000 --- a/csharp/sbe-benchmarks/Bench/SBE/BenchmarkState.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Org.SbeTool.Sbe.Dll; -using Uk.Co.Real_logic.Sbe.Benchmarks.Fix; - -namespace Org.SbeTool.Sbe.Benchmarks -{ - public class BenchmarkState - { - private readonly byte[] _eBuffer = new byte[1024]; - private readonly byte[] _dBuffer = new byte[1024]; - private readonly DirectBuffer _encodeBuffer; - private readonly DirectBuffer _decodeBuffer; - private readonly MarketDataIncrementalRefreshTrades _marketData = new MarketDataIncrementalRefreshTrades(); - private readonly MessageHeader _messageHeader = new MessageHeader(); - - public int BufferIndex - { - get { return 0; } - } - - public BenchmarkState() - { - _encodeBuffer = new DirectBuffer(_eBuffer); - _decodeBuffer = new DirectBuffer(_dBuffer); - MarketDataBenchmark.Encode(_messageHeader, _marketData, _decodeBuffer, BufferIndex); - } - - public DirectBuffer EncodeBuffer - { - get { return _encodeBuffer; } - } - - public DirectBuffer DecodeBuffer - { - get { return _decodeBuffer; } - } - - public MarketDataIncrementalRefreshTrades MarketData - { - get { return _marketData; } - } - - public MessageHeader MessageHeader - { - get { return _messageHeader; } - } - } -} \ No newline at end of file diff --git a/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark.cs b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark.cs new file mode 100644 index 0000000000..d0ba027173 --- /dev/null +++ b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark.cs @@ -0,0 +1,205 @@ +using System.Text; +using Baseline; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using Org.SbeTool.Sbe.Dll; + +namespace Org.SbeTool.Sbe.Benchmarks.Bench.Benchmarks +{ + [MemoryDiagnoser] + public class CarBenchmark + { + // string values are deliberately encoded outside of the benchmark + private static readonly byte[] VehicleCode = Encoding.GetEncoding(Car.VehicleCodeCharacterEncoding).GetBytes("CODE12"); + private static readonly byte[] ManufacturerCode = Encoding.GetEncoding(Engine.ManufacturerCodeCharacterEncoding).GetBytes("123"); + private static readonly byte[] Manufacturer = Encoding.GetEncoding(Car.ManufacturerCharacterEncoding).GetBytes("Honda"); + private static readonly byte[] Model = Encoding.GetEncoding(Car.ModelCharacterEncoding).GetBytes("Civic VTi"); + private static readonly byte[] ActivationCode = Encoding.GetEncoding(Car.ActivationCodeCharacterEncoding).GetBytes("code"); + private static readonly byte[] UrbanCycle = Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Urban Cycle"); + private static readonly byte[] CombinedCycle = Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Combined Cycle"); + private static readonly byte[] HighwayCycle = Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Highway Cycle"); + + private readonly byte[] _eBuffer = new byte[1024]; + private readonly byte[] _dBuffer = new byte[1024]; + private DirectBuffer _encodeBuffer; + private DirectBuffer _decodeBuffer; + private readonly Car _car = new Car(); + private readonly MessageHeader _messageHeader = new MessageHeader(); + + [GlobalSetup] + public void Setup() + { + _encodeBuffer = new DirectBuffer(_eBuffer); + _decodeBuffer = new DirectBuffer(_dBuffer); + Encode(_car, _decodeBuffer, 0); + } + + [Benchmark] + public int Encode() + { + return Encode(_car, _encodeBuffer, 0); + } + + [Benchmark] + public int Decode() + { + return Decode(_car, _decodeBuffer, 0); + } + + public int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _car.WrapForEncodeAndApplyHeader(directBuffer, bufferOffset, _messageHeader); + + const int srcOffset = 0; + + car.SerialNumber = 1234; + car.ModelYear = 2013; + car.Available = BooleanType.T; + car.Code = Baseline.Model.A; + car.SetVehicleCode(VehicleCode, srcOffset); + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + car.SetSomeNumbers(i, (uint)i); + } + + car.Extras = OptionalExtras.CruiseControl | OptionalExtras.SportsPack; + car.Engine.Capacity = 2000; + car.Engine.NumCylinders = 4; + car.Engine.SetManufacturerCode(ManufacturerCode, srcOffset); + car.Engine.Efficiency = 35; + car.Engine.BoosterEnabled = BooleanType.T; + car.Engine.Booster.BoostType = BoostType.NITROUS; + car.Engine.Booster.HorsePower = 200; + + var fuelFigures = car.FuelFiguresCount(3); + fuelFigures.Next(); + fuelFigures.Speed = 30; + fuelFigures.Mpg = 35.9f; + fuelFigures.SetUsageDescription(UrbanCycle); + + fuelFigures.Next(); + fuelFigures.Speed = 55; + fuelFigures.Mpg = 49.0f; + fuelFigures.SetUsageDescription(CombinedCycle); + + fuelFigures.Next(); + fuelFigures.Speed = 75; + fuelFigures.Mpg = 40.0f; + fuelFigures.SetUsageDescription(HighwayCycle); + + + Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2); + perfFigures.Next(); + perfFigures.OctaneRating = 95; + + Car.PerformanceFiguresGroup.AccelerationGroup acceleration = perfFigures.AccelerationCount(3).Next(); + acceleration.Mph = 30; + acceleration.Seconds = 4.0f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.5f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 12.2f; + + perfFigures.Next(); + perfFigures.OctaneRating = 99; + acceleration = perfFigures.AccelerationCount(3).Next(); + + acceleration.Mph = 30; + acceleration.Seconds = 3.8f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.1f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 11.8f; + + car.SetManufacturer(Manufacturer, srcOffset, Manufacturer.Length); + car.SetModel(Model, srcOffset, Model.Length); + car.SetActivationCode(ActivationCode, srcOffset, ActivationCode.Length); + + return car.Size; + } + + private readonly byte[] _buffer = new byte[128]; + + public int Decode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _messageHeader.Wrap(directBuffer, bufferOffset, 0); + + car.WrapForDecode(directBuffer, bufferOffset + MessageHeader.Size, _messageHeader.BlockLength, _messageHeader.Version); + + var templateId = Car.TemplateId; + var schemaVersion = Car.SchemaVersion; + var serialNumber = car.SerialNumber; + var modelYear = car.ModelYear; + var available = car.Available; + var code = car.Code; + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + var number = car.GetSomeNumbers(i); + } + + // strings are not actually decoded, only copied out into a buffer + car.GetVehicleCode(_buffer, 0); + + OptionalExtras extras = car.Extras; + var cruiseControl = (extras & OptionalExtras.CruiseControl) == OptionalExtras.CruiseControl; + var sportsPack = (extras & OptionalExtras.SportsPack) == OptionalExtras.SportsPack; + var sunRoof = (extras & OptionalExtras.SunRoof) == OptionalExtras.SunRoof; + + Engine engine = car.Engine; + var capacity = engine.Capacity; + var numCylinders = engine.NumCylinders; + var maxRpm = engine.MaxRpm; + for (int i = 0, size = Engine.ManufacturerCodeLength; i < size; i++) + { + engine.GetManufacturerCode(i); + } + + int length = engine.GetFuel(_buffer, 0, _buffer.Length); + + var efficiency = engine.Efficiency; + var boosterEnabled = engine.BoosterEnabled; + var boostType = engine.Booster.BoostType; + var horsePower = engine.Booster.HorsePower; + + var fuelFiguresGroup = car.FuelFigures; + while (fuelFiguresGroup.HasNext) + { + var fuelFigures = fuelFiguresGroup.Next(); + var speed = fuelFigures.Speed; + var mpg = fuelFigures.Mpg; + fuelFigures.GetUsageDescription(_buffer, 0, _buffer.Length); + } + + var performanceFiguresGroup = car.PerformanceFigures; + while (performanceFiguresGroup.HasNext) + { + performanceFiguresGroup.Next(); + var octanceRating = performanceFiguresGroup.OctaneRating; + + var accelerationGroup = performanceFiguresGroup.Acceleration; + for (int i = 0; i < accelerationGroup.Count; i++) + { + var acceleration = accelerationGroup.Next(); + var mpg = acceleration.Mph; + var seconds = acceleration.Seconds; + } + } + + length = car.GetManufacturer(_buffer, 0, _buffer.Length); + length = car.GetModel(_buffer, 0, _buffer.Length); + length = car.GetActivationCode(_buffer, 0, _buffer.Length); + + return car.Size; + } + } +} diff --git a/csharp/sbe-benchmarks/Bench/SBE/MarketDataBenchmark.cs b/csharp/sbe-benchmarks/Bench/SBE/MarketDataBenchmark.cs index 5797fbb493..79be30bf68 100644 --- a/csharp/sbe-benchmarks/Bench/SBE/MarketDataBenchmark.cs +++ b/csharp/sbe-benchmarks/Bench/SBE/MarketDataBenchmark.cs @@ -1,27 +1,48 @@ -using Org.SbeTool.Sbe.Dll; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using Org.SbeTool.Sbe.Dll; using Uk.Co.Real_logic.Sbe.Benchmarks.Fix; namespace Org.SbeTool.Sbe.Benchmarks { - public static class MarketDataBenchmark + [MemoryDiagnoser] + public class MarketDataBenchmark { - public static int Encode(MessageHeader messageHeader, - MarketDataIncrementalRefreshTrades marketData, - DirectBuffer buffer, - int bufferIndex) + private readonly byte[] _eBuffer = new byte[1024]; + private readonly byte[] _dBuffer = new byte[1024]; + private DirectBuffer _encodeBuffer; + private DirectBuffer _decodeBuffer; + private readonly MarketDataIncrementalRefreshTrades _marketData = new MarketDataIncrementalRefreshTrades(); + private readonly MessageHeader _messageHeader = new MessageHeader(); + + [GlobalSetup] + public void Setup() + { + _encodeBuffer = new DirectBuffer(_eBuffer); + _decodeBuffer = new DirectBuffer(_dBuffer); + Encode(_decodeBuffer, 0); + } + + [Benchmark] + public int Encode() + { + return Encode(_encodeBuffer, 0); + } + + public int Encode(DirectBuffer buffer, int bufferOffset) { - messageHeader.Wrap(buffer, bufferIndex, 0); - messageHeader.BlockLength = MarketDataIncrementalRefreshTrades.BlockLength; - messageHeader.TemplateId = MarketDataIncrementalRefreshTrades.TemplateId; - messageHeader.SchemaId = MarketDataIncrementalRefreshTrades.SchemaId; - messageHeader.Version = MarketDataIncrementalRefreshTrades.SchemaVersion; + _messageHeader.Wrap(buffer, bufferOffset, 0); + _messageHeader.BlockLength = MarketDataIncrementalRefreshTrades.BlockLength; + _messageHeader.TemplateId = MarketDataIncrementalRefreshTrades.TemplateId; + _messageHeader.SchemaId = MarketDataIncrementalRefreshTrades.SchemaId; + _messageHeader.Version = MarketDataIncrementalRefreshTrades.SchemaVersion; - marketData.WrapForEncode(buffer, bufferIndex + MessageHeader.Size); - marketData.TransactTime = 1234L; - marketData.EventTimeDelta = 987; - marketData.MatchEventIndicator = MatchEventIndicator.END_EVENT; + _marketData.WrapForEncode(buffer, bufferOffset + MessageHeader.Size); + _marketData.TransactTime = 1234L; + _marketData.EventTimeDelta = 987; + _marketData.MatchEventIndicator = MatchEventIndicator.END_EVENT; - var mdIncGrp = marketData.MdIncGrpCount(2); + var mdIncGrp = _marketData.MdIncGrpCount(2); mdIncGrp.Next(); mdIncGrp.TradeId = 1234L; @@ -43,25 +64,28 @@ public static int Encode(MessageHeader messageHeader, mdIncGrp.RptSeq = 1; mdIncGrp.AggressorSide = Side.SELL; - return marketData.Size; + return _marketData.Size; + } + + [Benchmark] + public int Decode() + { + return Decode(_decodeBuffer, 0); } - public static int Decode(MessageHeader messageHeader, - MarketDataIncrementalRefreshTrades marketData, - DirectBuffer buffer, - int bufferIndex) + public int Decode(DirectBuffer buffer, int bufferOffset) { - messageHeader.Wrap(buffer, bufferIndex, 0); + _messageHeader.Wrap(buffer, bufferOffset, 0); - int actingVersion = messageHeader.Version; - int actingBlockLength = messageHeader.BlockLength; + int actingVersion = _messageHeader.Version; + int actingBlockLength = _messageHeader.BlockLength; - marketData.WrapForDecode(buffer, bufferIndex + MessageHeader.Size, actingBlockLength, actingVersion); + _marketData.WrapForDecode(buffer, bufferOffset + MessageHeader.Size, actingBlockLength, actingVersion); - var transactTime = marketData.TransactTime; - var matchEventIndicator = marketData.MatchEventIndicator; + var transactTime = _marketData.TransactTime; + var matchEventIndicator = _marketData.MatchEventIndicator; - var mdIncGrpGroup = marketData.MdIncGrp; + var mdIncGrpGroup = _marketData.MdIncGrp; while (mdIncGrpGroup.HasNext) { mdIncGrpGroup.Next(); @@ -76,7 +100,7 @@ public static int Decode(MessageHeader messageHeader, var mdEntryType = mdIncGrpGroup.MdEntryType; } - return marketData.Size; + return _marketData.Size; } } } \ No newline at end of file diff --git a/csharp/sbe-benchmarks/Bench/SBE/SbePerfTestRunner.cs b/csharp/sbe-benchmarks/Bench/SBE/SbePerfTestRunner.cs deleted file mode 100644 index 5f0b79e3eb..0000000000 --- a/csharp/sbe-benchmarks/Bench/SBE/SbePerfTestRunner.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Diagnostics; -using Org.SbeTool.Sbe.Dll; -using Uk.Co.Real_logic.Sbe.Benchmarks.Fix; - -namespace Org.SbeTool.Sbe.Benchmarks -{ - public static class SbePerfTestRunner - { - static readonly double TicksToNanos = 1000 * 1000 * 1000 / (double)Stopwatch.Frequency; - - public static long PerfTestEncode(int runNumber) - { - const int reps = 10 * 1000 * 1000; - var state = new BenchmarkState(); - var gcCount = GC.CollectionCount(0); - - var sw = Stopwatch.StartNew(); - var size = 0; - for (int i = 0; i < reps; i++) - { - size = MarketDataBenchmark.Encode(state.MessageHeader, state.MarketData, state.EncodeBuffer, state.BufferIndex); - } - - var elapsedTicks = sw.ElapsedTicks; - var avgOpLatency = (long)((elapsedTicks / (double)reps) * TicksToNanos); - - Console.WriteLine("[{0}/Encode/SBE] - {1}(ns) average latency - message size: {2} - GC count: {3}", - runNumber, - avgOpLatency, - size + MessageHeader.Size, - GC.CollectionCount(0) - gcCount); - - return avgOpLatency; - } - - public static long PerfTestDecode(int runNumber) - { - const int reps = 10 * 1000 * 1000; - var state = new BenchmarkState(); - var marketDataSize = 0; - - var gcCount = GC.CollectionCount(0); - - var sw = Stopwatch.StartNew(); - for (int i = 0; i < reps; i++) - { - marketDataSize = MarketDataBenchmark.Decode(state.MessageHeader, state.MarketData, state.DecodeBuffer, state.BufferIndex); - } - - var elapsedTicks = sw.ElapsedTicks; - var avgOpLatency = (long)((elapsedTicks / (double)reps) * TicksToNanos); - - Console.WriteLine("[{0}/Decode/SBE] - {1}(ns) average latency - message size: {2} - GC count: {3}", - runNumber, - avgOpLatency, - marketDataSize + MessageHeader.Size, - GC.CollectionCount(0) - gcCount); - - return avgOpLatency; - } - } -} \ No newline at end of file diff --git a/csharp/sbe-benchmarks/Program.cs b/csharp/sbe-benchmarks/Program.cs index fbacc9b642..688229e63e 100644 --- a/csharp/sbe-benchmarks/Program.cs +++ b/csharp/sbe-benchmarks/Program.cs @@ -12,40 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; +using BenchmarkDotNet.Running; namespace Org.SbeTool.Sbe.Benchmarks { public class Program { - public static void Main() + public static void Main(string[] args) { - Console.WriteLine("WARM UP"); - SbePerfTestRunner.PerfTestEncode(-1); - SbePerfTestRunner.PerfTestDecode(-1); - - long sbeDecodeLatency = 0L; - long sbeEncodeLatency = 0L; - - Console.WriteLine(); - Console.WriteLine("Running ..."); - - const int runsCount = 5; - - for (int i = 0; i < runsCount; i++) - { - sbeEncodeLatency += SbePerfTestRunner.PerfTestEncode(i); - GC.Collect(2); - - sbeDecodeLatency += SbePerfTestRunner.PerfTestDecode(i); - GC.Collect(2); - } - - Console.WriteLine("##teamcity[buildStatisticValue key='AverageEncodeLatencyNanos' value='{0:0.0}']", (double) sbeEncodeLatency / runsCount); - Console.WriteLine("##teamcity[buildStatisticValue key='AverageDecodeLatencyNanos' value='{0:0.0}']", (double) sbeDecodeLatency / runsCount); - - Console.WriteLine("Press a key to continue..."); - Console.ReadKey(); + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); } } } diff --git a/csharp/sbe-benchmarks/sbe-benchmarks.csproj b/csharp/sbe-benchmarks/sbe-benchmarks.csproj index bcf5b9c996..8d10e86c4f 100644 --- a/csharp/sbe-benchmarks/sbe-benchmarks.csproj +++ b/csharp/sbe-benchmarks/sbe-benchmarks.csproj @@ -1,9 +1,9 @@  - net45;netcoreapp3.1 + net461;netcoreapp3.1 Org.SbeTool.Sbe.Benchmarks - Org.SbeTool.Sbe.Benchmarks + sbe-benchmarks exe Copyright (C) Bill Segall 2018, MarketFactory Inc 2017, Adaptive 2014. All rights reserved. SBE @@ -16,6 +16,8 @@ + +