diff options
author | chai <chaifix@163.com> | 2020-12-30 20:59:04 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2020-12-30 20:59:04 +0800 |
commit | e9ea621b93fbb58d9edfca8375918791637bbd52 (patch) | |
tree | 19ce3b1c1f2d51eda6878c9d0f2c9edc27f13650 /Impostor-dev/src/Impostor.Benchmarks |
+init
Diffstat (limited to 'Impostor-dev/src/Impostor.Benchmarks')
13 files changed, 1165 insertions, 0 deletions
diff --git a/Impostor-dev/src/Impostor.Benchmarks/.gitignore b/Impostor-dev/src/Impostor.Benchmarks/.gitignore new file mode 100644 index 0000000..1c2dac6 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/.gitignore @@ -0,0 +1 @@ +BenchmarkDotNet.Artifacts
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes.cs new file mode 100644 index 0000000..2aba724 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes.cs @@ -0,0 +1,172 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Impostor.Benchmarks.Data +{ + public class MessageReader_Bytes + { + public byte Tag { get; } + public byte[] Buffer { get; } + public int Position { get; set; } + public int Length { get; set; } + public int BytesRemaining => this.Length - this.Position; + + public MessageReader_Bytes(byte[] buffer, int position = 0, int length = 0) + { + Tag = byte.MaxValue; + Buffer = buffer; + Position = position; + Length = length; + } + + public MessageReader_Bytes(byte tag, byte[] buffer, int position = 0, int length = 0) + { + Tag = tag; + Buffer = buffer; + Position = position; + Length = length; + } + + public MessageReader_Bytes ReadMessage() + { + var length = ReadUInt16(); + var tag = FastByte(); + var pos = Position; + + Position += length; + return new MessageReader_Bytes(tag, Buffer, pos, length); + } + + public bool ReadBoolean() + { + byte val = FastByte(); + return val != 0; + } + + public sbyte ReadSByte() + { + return (sbyte)FastByte(); + } + + public byte ReadByte() + { + return FastByte(); + } + + public ushort ReadUInt16() + { + return (ushort)(this.FastByte() | + this.FastByte() << 8); + } + + public short ReadInt16() + { + return (short)(this.FastByte() | + this.FastByte() << 8); + } + + public uint ReadUInt32() + { + return this.FastByte() + | (uint)this.FastByte() << 8 + | (uint)this.FastByte() << 16 + | (uint)this.FastByte() << 24; + } + + public int ReadInt32() + { + return this.FastByte() + | this.FastByte() << 8 + | this.FastByte() << 16 + | this.FastByte() << 24; + } + + public unsafe float ReadSingle() + { + float output = 0; + fixed (byte* bufPtr = &this.Buffer[Position]) + { + byte* outPtr = (byte*)&output; + + *outPtr = *bufPtr; + *(outPtr + 1) = *(bufPtr + 1); + *(outPtr + 2) = *(bufPtr + 2); + *(outPtr + 3) = *(bufPtr + 3); + } + + this.Position += 4; + return output; + } + + public string ReadString() + { + var len = this.ReadPackedInt32(); + + if (this.BytesRemaining < len) + { + throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); + } + + var output = Encoding.UTF8.GetString(this.Buffer, Position, len); + this.Position += len; + return output; + } + + public Span<byte> ReadBytesAndSize() + { + var len = ReadPackedInt32(); + return ReadBytes(len); + } + + public Span<byte> ReadBytes(int length) + { + var output = Buffer.AsSpan(Position, length); + Position += length; + return output; + } + + public int ReadPackedInt32() + { + return (int)ReadPackedUInt32(); + } + + public uint ReadPackedUInt32() + { + bool readMore = true; + int shift = 0; + uint output = 0; + + while (readMore) + { + byte b = FastByte(); + if (b >= 0x80) + { + readMore = true; + b ^= 0x80; + } + else + { + readMore = false; + } + + output |= (uint)(b << shift); + shift += 7; + } + + return output; + } + + public MessageReader_Bytes Slice(int start, int length) + { + return new MessageReader_Bytes(Tag, Buffer, start, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte FastByte() + { + return Buffer[Position++]; + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled.cs new file mode 100644 index 0000000..1103336 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Impostor.Benchmarks.Data +{ + public class MessageReader_Bytes_Pooled + { + private static ConcurrentQueue<MessageReader_Bytes_Pooled> _readers; + + static MessageReader_Bytes_Pooled() + { + var instances = new List<MessageReader_Bytes_Pooled>(); + + for (var i = 0; i < 10000; i++) + { + instances.Add(new MessageReader_Bytes_Pooled()); + } + + _readers = new ConcurrentQueue<MessageReader_Bytes_Pooled>(instances); + } + + public byte Tag { get; set; } + public byte[] Buffer { get; set; } + public int Position { get; set; } + public int Length { get; set; } + public int BytesRemaining => this.Length - this.Position; + + public void Update(byte[] buffer, int position = 0, int length = 0) + { + Tag = byte.MaxValue; + Buffer = buffer; + Position = position; + Length = length; + } + + public void Update(byte tag, byte[] buffer, int position = 0, int length = 0) + { + Tag = tag; + Buffer = buffer; + Position = position; + Length = length; + } + + public MessageReader_Bytes_Pooled ReadMessage() + { + var length = ReadUInt16(); + var tag = FastByte(); + var pos = Position; + + Position += length; + + if (!_readers.TryDequeue(out var result)) + { + throw new Exception("Failed to get pooled instance"); + } + + result.Update(tag, Buffer, pos, length); + + return result; + } + + public bool ReadBoolean() + { + byte val = FastByte(); + return val != 0; + } + + public sbyte ReadSByte() + { + return (sbyte)FastByte(); + } + + public byte ReadByte() + { + return FastByte(); + } + + public ushort ReadUInt16() + { + return (ushort)(this.FastByte() | + this.FastByte() << 8); + } + + public short ReadInt16() + { + return (short)(this.FastByte() | + this.FastByte() << 8); + } + + public uint ReadUInt32() + { + return this.FastByte() + | (uint)this.FastByte() << 8 + | (uint)this.FastByte() << 16 + | (uint)this.FastByte() << 24; + } + + public int ReadInt32() + { + return this.FastByte() + | this.FastByte() << 8 + | this.FastByte() << 16 + | this.FastByte() << 24; + } + + public unsafe float ReadSingle() + { + float output = 0; + fixed (byte* bufPtr = &this.Buffer[Position]) + { + byte* outPtr = (byte*)&output; + + *outPtr = *bufPtr; + *(outPtr + 1) = *(bufPtr + 1); + *(outPtr + 2) = *(bufPtr + 2); + *(outPtr + 3) = *(bufPtr + 3); + } + + this.Position += 4; + return output; + } + + public string ReadString() + { + var len = this.ReadPackedInt32(); + + if (this.BytesRemaining < len) + { + throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); + } + + var output = Encoding.UTF8.GetString(this.Buffer, Position, len); + this.Position += len; + return output; + } + + public Span<byte> ReadBytesAndSize() + { + var len = ReadPackedInt32(); + return ReadBytes(len); + } + + public Span<byte> ReadBytes(int length) + { + var output = Buffer.AsSpan(Position, length); + Position += length; + return output; + } + + public int ReadPackedInt32() + { + return (int)ReadPackedUInt32(); + } + + public uint ReadPackedUInt32() + { + bool readMore = true; + int shift = 0; + uint output = 0; + + while (readMore) + { + byte b = FastByte(); + if (b >= 0x80) + { + readMore = true; + b ^= 0x80; + } + else + { + readMore = false; + } + + output |= (uint)(b << shift); + shift += 7; + } + + return output; + } + + public MessageReader_Bytes_Pooled Slice(int start, int length) + { + if (!_readers.TryDequeue(out var result)) + { + throw new Exception("Failed to get pooled instance"); + } + + result.Update(Tag, Buffer, start, length); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte FastByte() + { + return Buffer[Position++]; + } + + public static MessageReader_Bytes_Pooled Get(byte[] data) + { + if (!_readers.TryDequeue(out var result)) + { + throw new Exception("Failed to get pooled instance"); + } + + result.Update(data); + + return result; + } + + public static void Return(MessageReader_Bytes_Pooled instance) + { + _readers.Enqueue(instance); + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled_Improved.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled_Improved.cs new file mode 100644 index 0000000..0066847 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled_Improved.cs @@ -0,0 +1,90 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.Extensions.ObjectPool; + +namespace Impostor.Benchmarks.Data +{ + public class MessageReader_Bytes_Pooled_Improved : IDisposable + { + private readonly ObjectPool<MessageReader_Bytes_Pooled_Improved> _pool; + + public MessageReader_Bytes_Pooled_Improved(ObjectPool<MessageReader_Bytes_Pooled_Improved> pool) + { + _pool = pool; + } + + public byte Tag { get; set; } + public byte[] Buffer { get; set; } + public int Position { get; set; } + public int Length { get; set; } + public int BytesRemaining => this.Length - this.Position; + + public void Update(byte[] buffer, int position = 0, int length = 0) + { + Tag = byte.MaxValue; + Buffer = buffer; + Position = position; + Length = length; + } + + public void Update(byte tag, byte[] buffer, int position = 0, int length = 0) + { + Tag = tag; + Buffer = buffer; + Position = position; + Length = length; + } + + public MessageReader_Bytes_Pooled_Improved ReadMessage() + { + var length = ReadUInt16(); + var tag = ReadByte(); + var pos = Position; + + Position += length; + + var result = _pool.Get(); + result.Update(tag, Buffer, pos, length); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte ReadByte() + { + return Buffer[Position++]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ushort ReadUInt16() + { + var res = BinaryPrimitives.ReadUInt16LittleEndian(Buffer.AsSpan(Position)); + Position += sizeof(ushort); + return res; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int ReadInt32() + { + var res = BinaryPrimitives.ReadInt32LittleEndian(Buffer.AsSpan(Position)); + Position += sizeof(int); + return res; + } + + public MessageReader_Bytes_Pooled_Improved Slice(int start, int length) + { + var result = _pool.Get(); + result.Update(Tag, Buffer, start, length); + return result; + } + + public void Dispose() + { + _pool.Return(this); + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/MessageWriter.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageWriter.cs new file mode 100644 index 0000000..94ff441 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/MessageWriter.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using Impostor.Api.Games; +using Impostor.Api.Net.Messages; + +namespace Impostor.Benchmarks.Data +{ + public class MessageWriter + { + private static int BufferSize = 64000; + + public MessageType SendOption { get; private set; } + + private Stack<int> messageStarts = new Stack<int>(); + + public MessageWriter(byte[] buffer) + { + this.Buffer = buffer; + this.Length = this.Buffer.Length; + } + + public MessageWriter(int bufferSize) + { + this.Buffer = new byte[bufferSize]; + } + + public byte[] Buffer { get; } + public int Length { get; set; } + public int Position { get; set; } + + public byte[] ToByteArray(bool includeHeader) + { + if (includeHeader) + { + byte[] output = new byte[this.Length]; + System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length); + return output; + } + else + { + switch (this.SendOption) + { + case MessageType.Reliable: + { + byte[] output = new byte[this.Length - 3]; + System.Buffer.BlockCopy(this.Buffer, 3, output, 0, this.Length - 3); + return output; + } + case MessageType.Unreliable: + { + byte[] output = new byte[this.Length - 1]; + System.Buffer.BlockCopy(this.Buffer, 1, output, 0, this.Length - 1); + return output; + } + default: + throw new ArgumentOutOfRangeException(); + } + } + + throw new NotImplementedException(); + } + + public bool HasBytes(int expected) + { + if (this.SendOption == MessageType.Unreliable) + { + return this.Length > 1 + expected; + } + + return this.Length > 3 + expected; + } + + public void Write(GameCode value) + { + this.Write(value.Value); + } + + /// + public void StartMessage(byte typeFlag) + { + messageStarts.Push(this.Position); + this.Position += 2; // Skip for size + this.Write(typeFlag); + } + + /// + public void EndMessage() + { + var lastMessageStart = messageStarts.Pop(); + ushort length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte + this.Buffer[lastMessageStart] = (byte)length; + this.Buffer[lastMessageStart + 1] = (byte)(length >> 8); + } + + /// + public void CancelMessage() + { + this.Position = this.messageStarts.Pop(); + this.Length = this.Position; + } + + public void Clear(MessageType sendOption) + { + this.messageStarts.Clear(); + this.SendOption = sendOption; + this.Buffer[0] = (byte)sendOption; + switch (sendOption) + { + default: + case MessageType.Unreliable: + this.Length = this.Position = 1; + break; + + case MessageType.Reliable: + this.Length = this.Position = 3; + break; + } + } + + #region WriteMethods + + public void Write(bool value) + { + this.Buffer[this.Position++] = (byte)(value ? 1 : 0); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(sbyte value) + { + this.Buffer[this.Position++] = (byte)value; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte value) + { + this.Buffer[this.Position++] = value; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(short value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(ushort value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(uint value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(int value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + if (this.Position > this.Length) this.Length = this.Position; + } + + public unsafe void Write(float value) + { + fixed (byte* ptr = &this.Buffer[this.Position]) + { + byte* valuePtr = (byte*)&value; + + *ptr = *valuePtr; + *(ptr + 1) = *(valuePtr + 1); + *(ptr + 2) = *(valuePtr + 2); + *(ptr + 3) = *(valuePtr + 3); + } + + this.Position += 4; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(string value) + { + var bytes = UTF8Encoding.UTF8.GetBytes(value); + this.WritePacked(bytes.Length); + this.Write(bytes); + } + + public void Write(IPAddress value) + { + this.Write(value.GetAddressBytes()); + } + + public void WriteBytesAndSize(byte[] bytes) + { + this.WritePacked((uint)bytes.Length); + this.Write(bytes); + } + + public void WriteBytesAndSize(byte[] bytes, int length) + { + this.WritePacked((uint)length); + this.Write(bytes, length); + } + + public void WriteBytesAndSize(byte[] bytes, int offset, int length) + { + this.WritePacked((uint)length); + this.Write(bytes, offset, length); + } + + public void Write(ReadOnlyMemory<byte> data) + { + Write(data.Span); + } + + public void Write(ReadOnlySpan<byte> bytes) + { + bytes.CopyTo(this.Buffer.AsSpan(this.Position, bytes.Length)); + + this.Position += bytes.Length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte[] bytes) + { + Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length); + this.Position += bytes.Length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte[] bytes, int offset, int length) + { + Array.Copy(bytes, offset, this.Buffer, this.Position, length); + this.Position += length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte[] bytes, int length) + { + Array.Copy(bytes, 0, this.Buffer, this.Position, length); + this.Position += length; + if (this.Position > this.Length) this.Length = this.Position; + } + + /// + public void WritePacked(int value) + { + this.WritePacked((uint)value); + } + + /// + public void WritePacked(uint value) + { + do + { + byte b = (byte)(value & 0xFF); + if (value >= 0x80) + { + b |= 0x80; + } + + this.Write(b); + value >>= 7; + } while (value > 0); + } + + #endregion WriteMethods + + public void Write(MessageWriter msg, bool includeHeader) + { + int offset = 0; + if (!includeHeader) + { + switch (msg.SendOption) + { + case MessageType.Unreliable: + offset = 1; + break; + + case MessageType.Reliable: + offset = 3; + break; + } + } + + this.Write(msg.Buffer, offset, msg.Length - offset); + } + + public unsafe static bool IsLittleEndian() + { + byte b; + unsafe + { + int i = 1; + byte* bp = (byte*)&i; + b = *bp; + } + + return b == 1; + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/Pool/MessageReader_Bytes_Pooled_ImprovedPolicy.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/Pool/MessageReader_Bytes_Pooled_ImprovedPolicy.cs new file mode 100644 index 0000000..802fcaa --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/Pool/MessageReader_Bytes_Pooled_ImprovedPolicy.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.ObjectPool; + +namespace Impostor.Benchmarks.Data.Pool +{ + public class MessageReader_Bytes_Pooled_ImprovedPolicy : IPooledObjectPolicy<MessageReader_Bytes_Pooled_Improved> + { + private readonly IServiceProvider _serviceProvider; + + public MessageReader_Bytes_Pooled_ImprovedPolicy(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public MessageReader_Bytes_Pooled_Improved Create() + { + return new MessageReader_Bytes_Pooled_Improved(_serviceProvider.GetRequiredService<ObjectPool<MessageReader_Bytes_Pooled_Improved>>()); + } + + public bool Return(MessageReader_Bytes_Pooled_Improved obj) + { + return true; + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReaderOwner.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReaderOwner.cs new file mode 100644 index 0000000..402fbaf --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReaderOwner.cs @@ -0,0 +1,22 @@ +using System; + +namespace Impostor.Benchmarks.Data.Span +{ + public class MessageReaderOwner + { + private readonly Memory<byte> _data; + + public MessageReaderOwner(Memory<byte> data) + { + _data = data; + } + + public int Position { get; internal set; } + public int Length => _data.Length; + + public MessageReader_Span CreateReader() + { + return new MessageReader_Span(this, byte.MaxValue, _data.Span.Slice(Position)); + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReader_Span.cs b/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReader_Span.cs new file mode 100644 index 0000000..5636dbe --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReader_Span.cs @@ -0,0 +1,50 @@ +using System; +using System.Buffers.Binary; +using Impostor.Hazel; + +namespace Impostor.Benchmarks.Data.Span +{ + public ref struct MessageReader_Span + { + private readonly MessageReaderOwner _owner; + private readonly byte _tag; + private readonly Span<byte> _data; + + public MessageReader_Span(MessageReaderOwner owner, byte tag, Span<byte> data) + { + _owner = owner; + _tag = tag; + _data = data; + } + + public MessageReader_Span ReadMessage() + { + var length = ReadUInt16(); + var tag = ReadByte(); + var pos = _owner.Position; + + _owner.Position += length; + + return new MessageReader_Span(_owner, tag, _data.Slice(3, length)); + } + + public byte ReadByte() + { + return _data[_owner.Position++]; + } + + public ushort ReadUInt16() + { + var output = BinaryPrimitives.ReadUInt16LittleEndian(_data.Slice(_owner.Position)); + _owner.Position += sizeof(ushort); + return output; + } + + public int ReadInt32() + { + var output = BinaryPrimitives.ReadInt32LittleEndian(_data.Slice(_owner.Position)); + _owner.Position += sizeof(int); + return output; + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Extensions/SpanExtensions.cs b/Impostor-dev/src/Impostor.Benchmarks/Extensions/SpanExtensions.cs new file mode 100644 index 0000000..8fbceeb --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Extensions/SpanExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace Impostor.Benchmarks.Extensions +{ + public static class SpanExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<byte> ReadMessage(this Span<byte> input) + { + var length = BinaryPrimitives.ReadUInt16LittleEndian(input); + var tag = input[2]; + + return input.Slice(3, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ReadUInt16(this ref ReadOnlySpan<byte> input) + { + return BinaryPrimitives.ReadUInt16LittleEndian(input); + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj b/Impostor-dev/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj new file mode 100644 index 0000000..6f19bda --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj @@ -0,0 +1,17 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>Exe</OutputType> + <TargetFramework>net5.0</TargetFramework> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="BenchmarkDotNet" Version="0.12.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Impostor.Server\Impostor.Server.csproj" /> + </ItemGroup> + +</Project> diff --git a/Impostor-dev/src/Impostor.Benchmarks/Program.cs b/Impostor-dev/src/Impostor.Benchmarks/Program.cs new file mode 100644 index 0000000..3ce7383 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Program.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Running; +using Impostor.Benchmarks.Tests; + +namespace Impostor.Benchmarks +{ + internal static class Program + { + private static void Main(string[] args) + { + // BenchmarkRunner.Run<EventManagerBenchmark>( + // DefaultConfig.Instance + // .AddDiagnoser(MemoryDiagnoser.Default) + // ); + + BenchmarkRunner.Run<MessageReaderBenchmark>( + DefaultConfig.Instance + .AddDiagnoser(MemoryDiagnoser.Default) + ); + } + } +} diff --git a/Impostor-dev/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs b/Impostor-dev/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs new file mode 100644 index 0000000..0675080 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs @@ -0,0 +1,77 @@ +// using System.Threading.Tasks; +// using BenchmarkDotNet.Attributes; +// using Impostor.Api.Events; +// using Impostor.Api.Events.Managers; +// using Impostor.Server.Events; +// using Microsoft.Extensions.DependencyInjection; +// +// namespace Impostor.Benchmarks.Tests +// { +// public class EventManagerBenchmark +// { +// private IEventManager _eventManager; +// private IGameEvent _event; +// +// [GlobalSetup] +// public void Setup() +// { +// var services = new ServiceCollection(); +// +// services.AddLogging(); +// services.AddSingleton<IEventManager, EventManager>(); +// +// _event = new GameStartedEvent(null); +// _eventManager = services.BuildServiceProvider().GetRequiredService<IEventManager>(); +// _eventManager.RegisterListener(new EventListener()); +// _eventManager.RegisterListener(new EventListener()); +// _eventManager.RegisterListener(new EventListener()); +// _eventManager.RegisterListener(new EventListener()); +// _eventManager.RegisterListener(new EventListener()); +// } +// +// [Benchmark] +// public async Task Run_1() +// { +// for (var i = 0; i < 1; i++) +// { +// await _eventManager.CallAsync(_event); +// } +// } +// +// [Benchmark] +// public async Task Run_1000() +// { +// for (var i = 0; i < 1000; i++) +// { +// await _eventManager.CallAsync(_event); +// } +// } +// +// [Benchmark] +// public async Task Run_10000() +// { +// for (var i = 0; i < 10000; i++) +// { +// await _eventManager.CallAsync(_event); +// } +// } +// +// [Benchmark] +// public async Task Run_100000() +// { +// for (var i = 0; i < 100000; i++) +// { +// await _eventManager.CallAsync(_event); +// } +// } +// +// private class EventListener : IEventListener +// { +// [EventListener] +// public void OnGameStarted(IGameStartedEvent e) +// { +// +// } +// } +// } +// } diff --git a/Impostor-dev/src/Impostor.Benchmarks/Tests/MessageReaderBenchmark.cs b/Impostor-dev/src/Impostor.Benchmarks/Tests/MessageReaderBenchmark.cs new file mode 100644 index 0000000..abf1642 --- /dev/null +++ b/Impostor-dev/src/Impostor.Benchmarks/Tests/MessageReaderBenchmark.cs @@ -0,0 +1,132 @@ +using System; +using System.Buffers.Binary; +using BenchmarkDotNet.Attributes; +using Impostor.Benchmarks.Data; +using Impostor.Benchmarks.Data.Pool; +using Impostor.Benchmarks.Extensions; +using Impostor.Hazel; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.ObjectPool; +using MessageWriter = Impostor.Benchmarks.Data.MessageWriter; + +namespace Impostor.Benchmarks.Tests +{ + public class MessageReaderBenchmark + { + private byte[] _data; + private ObjectPool<MessageReader_Bytes_Pooled_Improved> _pool; + + [GlobalSetup] + public void Setup() + { + var message = new MessageWriter(1024); + + message.StartMessage(1); + message.Write((ushort)3100); + message.Write((byte)100); + message.Write((int) int.MaxValue); + message.WritePacked(int.MaxValue); + message.EndMessage(); + + _data = message.ToByteArray(true); + + MessageReader_Bytes_Pooled.Return(MessageReader_Bytes_Pooled.Get(_data)); + + // Services + var services = new ServiceCollection(); + + services.AddSingleton<ObjectPoolProvider>(new DefaultObjectPoolProvider()); + services.AddSingleton(serviceProvider => + { + var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>(); + var policy = new MessageReader_Bytes_Pooled_ImprovedPolicy(serviceProvider); + return provider.Create(policy); + }); + + _pool = services + .BuildServiceProvider() + .GetRequiredService<ObjectPool<MessageReader_Bytes_Pooled_Improved>>(); + } + + [Benchmark] + public void Span_Run_1_000_000() + { + for (var i = 0; i < 1_000_000; i++) + { + var span = _data.AsSpan(); + var inner = span.ReadMessage(); + + _ = BinaryPrimitives.ReadUInt16LittleEndian(inner); + _ = inner[2]; + _ = BinaryPrimitives.ReadInt32LittleEndian(inner.Slice(3)); + } + } + + // [Benchmark] + // public void Normal_Run_1_000_000() + // { + // for (var i = 0; i < 1_000_000; i++) + // { + // var reader = new MessageReader(_data); + // var inner = reader.ReadMessage(); + // + // _ = inner.ReadUInt16(); + // _ = inner.ReadByte(); + // _ = inner.ReadInt32(); + // // inner.ReadPackedInt32(); + // } + // } + + [Benchmark] + public void Bytes_Run_1_000_000() + { + for (var i = 0; i < 1_000_000; i++) + { + var reader = new MessageReader_Bytes(_data); + var inner = reader.ReadMessage(); + + _ = inner.ReadUInt16(); + _ = inner.ReadByte(); + _ = inner.ReadInt32(); + // inner.ReadPackedInt32(); + } + } + + [Benchmark] + public void Pooled_Bytes_Run_1_000_000() + { + for (var i = 0; i < 1_000_000; i++) + { + var reader = MessageReader_Bytes_Pooled.Get(_data); + var inner = reader.ReadMessage(); + + _ = inner.ReadUInt16(); + _ = inner.ReadByte(); + _ = inner.ReadInt32(); + // inner.ReadPackedInt32(); + + MessageReader_Bytes_Pooled.Return(inner); + MessageReader_Bytes_Pooled.Return(reader); + } + } + + [Benchmark] + public void Improved_Pooled_Bytes_Run_1_000_000() + { + using (var reader = _pool.Get()) + { + for (var i = 0; i < 1_000_000; i++) + { + reader.Update(_data); + + using (var inner = reader.ReadMessage()) + { + _ = inner.ReadUInt16(); + _ = inner.ReadByte(); + _ = inner.ReadInt32(); + } + } + } + } + } +} |