summaryrefslogtreecommitdiff
path: root/Impostor-dev/src/Impostor.Benchmarks
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2020-12-30 20:59:04 +0800
committerchai <chaifix@163.com>2020-12-30 20:59:04 +0800
commite9ea621b93fbb58d9edfca8375918791637bbd52 (patch)
tree19ce3b1c1f2d51eda6878c9d0f2c9edc27f13650 /Impostor-dev/src/Impostor.Benchmarks
+init
Diffstat (limited to 'Impostor-dev/src/Impostor.Benchmarks')
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/.gitignore1
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes.cs172
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled.cs220
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/MessageReader_Bytes_Pooled_Improved.cs90
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/MessageWriter.cs311
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/Pool/MessageReader_Bytes_Pooled_ImprovedPolicy.cs26
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReaderOwner.cs22
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Data/Span/MessageReader_Span.cs50
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Extensions/SpanExtensions.cs24
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Impostor.Benchmarks.csproj17
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Program.cs23
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Tests/EventManagerBenchmark.cs77
-rw-r--r--Impostor-dev/src/Impostor.Benchmarks/Tests/MessageReaderBenchmark.cs132
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();
+ }
+ }
+ }
+ }
+ }
+}