From 09f8dda9909d1a87aa60cdc6ac6bdca25692dbf4 Mon Sep 17 00:00:00 2001 From: Timothy Schenk Date: Thu, 19 Sep 2024 05:09:25 +0200 Subject: [PATCH] chore: hexpats + MemCasting benchmarks --- Benchmarks/MemCasting.cs | 178 +++++++++++++++++++++ hex-patterns/basecharacterstatdata.hexproj | Bin 75776 -> 20480 bytes 2 files changed, 178 insertions(+) create mode 100644 Benchmarks/MemCasting.cs diff --git a/Benchmarks/MemCasting.cs b/Benchmarks/MemCasting.cs new file mode 100644 index 0000000..85d724e --- /dev/null +++ b/Benchmarks/MemCasting.cs @@ -0,0 +1,178 @@ +// Licensed to Timothy Schenk under the GNU AGPL Version 3 License. + +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Order; + +namespace Benchmarks; + +[Config(typeof(GenericConfig))] +[Orderer(SummaryOrderPolicy.FastestToSlowest)] +public class MemCasting +{ + private byte[] _data = null!; + private byte[] _arrayData = null!; + + [Params(1000, 100000, 1000000)] public int N; + [GlobalSetup] + public void Setup() + { + _data = new byte[64]; + _arrayData = new byte[N * 64]; + Random.Shared.NextBytes(_data); + Random.Shared.NextBytes(_arrayData); + } + private static ElementalStats ReadElementalStatsMemoryMarshal(ref Span data) + { + return MemoryMarshal.Cast(data.Slice(0, 64))[0]; + } + private static ElementalStats ReadElementalStatsMemoryMarshal2(ref Span data) + { + return MemoryMarshal.Read(data.Slice(0, 64)); + } + private static ElementalStats ReadElementalStatsNew(ref Span data) + { + return new ElementalStats + { + MinimumFireDamage = BitConverter.ToInt32(data.Slice(0, 4)), // 140 -> 144 + MinimumWaterDamage = BitConverter.ToInt32(data.Slice(4, 4)), // 144 -> 148 + MinimumDarkDamage = BitConverter.ToInt32(data.Slice(8, 4)), // 148 -> 152 + MinimumHolyDamage = BitConverter.ToInt32(data.Slice(12, 4)), // 152 -> 156 + MaximumFireDamage = BitConverter.ToInt32(data.Slice(16, 4)), // 156 -> 160 + MaximumWaterDamage = BitConverter.ToInt32(data.Slice(20, 4)), // 160 -> 164 + MaximumDarkDamage = BitConverter.ToInt32(data.Slice(24, 4)), // 164 -> 168 + MaximumHolyDamage = BitConverter.ToInt32(data.Slice(28, 4)), // 168 -> 172 + ElementFire = BitConverter.ToUInt32(data.Slice(32, 4)), // 172 -> 176 + ElementWater = BitConverter.ToUInt32(data.Slice(36, 4)), // 176 -> 180 + ElementDark = BitConverter.ToUInt32(data.Slice(40, 4)), // 180 -> 184 + ElementHoly = BitConverter.ToUInt32(data.Slice(44, 4)), // 184 -> 188 + FireResistance = BitConverter.ToInt32(data.Slice(48, 4)), // 188 -> 192 + WaterResistance = BitConverter.ToInt32(data.Slice(52, 4)), // 192 -> 196 + DarkResistance = BitConverter.ToInt32(data.Slice(56, 4)), // 196 -> 200 + HolyResistance = BitConverter.ToInt32(data.Slice(60, 4)) // 200 -> 204 + }; + } + + [Benchmark] + public Span MemoryMarshalCastArray() + { + var data = _arrayData.AsSpan(); + var elements = MemoryMarshal.Cast(data); + + return elements; + } + + [Benchmark] + public Span MemoryMarshalReadArray() + { + Span statsArr = stackalloc ElementalStats[N + 1]; + var data = _data.AsSpan(); + for (int i = 0; i <= N; i++) + { + statsArr[i] = ReadElementalStatsMemoryMarshal2(ref data); + } + + return statsArr.ToArray(); + } + [Benchmark] + public Span ManualSpanSlicingArray() + { + Span statsArr = stackalloc ElementalStats[N + 1]; + var data = _data.AsSpan(); + for (int i = 0; i <= N; i++) + { + statsArr[i] = ReadElementalStatsNew(ref data); + } + + return statsArr.ToArray(); + } + + [Benchmark] + public ElementalStats MemoryMarshalCastSingle() + { + var data = _data.AsSpan(); + return ReadElementalStatsMemoryMarshal(ref data); + } + + [Benchmark] + public ElementalStats MemoryMarshalReadSingle() + { + var data = _data.AsSpan(); + return ReadElementalStatsMemoryMarshal2(ref data); + } + [Benchmark] + public ElementalStats ManualSpanSlicingSingle() + { + var data = _data.AsSpan(); + return ReadElementalStatsNew(ref data); + } + + [StructLayout(LayoutKind.Explicit, Size = 64)] + public struct ElementalStats + { + [FieldOffset(0)] + [MarshalAs(UnmanagedType.I4)] + public int MinimumFireDamage; + + [FieldOffset(4)] + [MarshalAs(UnmanagedType.I4)] + public int MinimumWaterDamage; + + [FieldOffset(8)] + [MarshalAs(UnmanagedType.I4)] + public int MinimumDarkDamage; + + [FieldOffset(12)] + [MarshalAs(UnmanagedType.I4)] + public int MinimumHolyDamage; + + [FieldOffset(16)] + [MarshalAs(UnmanagedType.I4)] + public int MaximumFireDamage; + + [FieldOffset(20)] + [MarshalAs(UnmanagedType.I4)] + public int MaximumWaterDamage; + + [FieldOffset(24)] + [MarshalAs(UnmanagedType.I4)] + public int MaximumDarkDamage; + + [FieldOffset(28)] + [MarshalAs(UnmanagedType.I4)] + public int MaximumHolyDamage; + + [FieldOffset(32)] + [MarshalAs(UnmanagedType.U4)] + public uint ElementFire; + + [FieldOffset(36)] + [MarshalAs(UnmanagedType.U4)] + public uint ElementWater; + + [FieldOffset(40)] + [MarshalAs(UnmanagedType.U4)] + public uint ElementDark; + + [FieldOffset(44)] + [MarshalAs(UnmanagedType.U4)] + public uint ElementHoly; + + [FieldOffset(48)] + [MarshalAs(UnmanagedType.I4)] + public int FireResistance; + + [FieldOffset(52)] + [MarshalAs(UnmanagedType.I4)] + public int WaterResistance; + + [FieldOffset(56)] + [MarshalAs(UnmanagedType.I4)] + public int DarkResistance; + + [FieldOffset(60)] + [MarshalAs(UnmanagedType.I4)] + public int HolyResistance; + } + +} diff --git a/hex-patterns/basecharacterstatdata.hexproj b/hex-patterns/basecharacterstatdata.hexproj index 31093c08bcdbb02df0f760811ea2b1410846acde..41b4b6847558eca85e1d7b769882ed5b19f3eb82 100644 GIT binary patch delta 687 zcmZva%SyvQ6o!qhQu+X*3n7StRU?z>ByH(Rw2Oj8(C&(hv6<@7WD;}f1(fy`G)rBG zOPB3SxDk8>U%-Vcb>&R7)(`_3&Yb!F{O6p^@HsualSYddnG`C_gadhTPSx}oIg~b< zW3Vns*DK58+R9kElEMAz{OI*!Hmw1G!r|6Z3jX0nZx8d?Jd^|0MveS&*e!1iU`gH7 z;Bh%C^T)x4LD^|T)k{B_faBP4OiH}EeblC=SvBHp@egTHwof|5JZS9*O%tD(Bof6X z5d3NeQ8n84xiE9dc|cuaRDELA`wp3&@T&Fc&ceOOYMN=(&>{FXH1_`hj15a!gczdMh6PNn^2 zj9pX|8C5#i6ICUOQbEkd!VK@)p8pT1$3W6*k*0r$eKC>n<$a-|^8OUaDWwoaJP;f4 zA_XG8AVpOQVucFisHR1c0*iSb@E>-2MVw C)5SLc literal 75776 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 F{vUicCzSvI