diff options
Diffstat (limited to 'Impostor-dev/src/Impostor.Benchmarks/Data')
7 files changed, 891 insertions, 0 deletions
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; + } + } +} |