chore: hexpats + MemCasting benchmarks

This commit is contained in:
Timothy Schenk 2024-09-19 05:09:25 +02:00
parent 863332b806
commit 09f8dda990
Signed by: rainote
SSH key fingerprint: SHA256:pnkNSDwpAnaip00xaZlVFHKKsS7T8UtOomMzvs0yITE
2 changed files with 178 additions and 0 deletions

178
Benchmarks/MemCasting.cs Normal file
View file

@ -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<byte> data)
{
return MemoryMarshal.Cast<byte, ElementalStats>(data.Slice(0, 64))[0];
}
private static ElementalStats ReadElementalStatsMemoryMarshal2(ref Span<byte> data)
{
return MemoryMarshal.Read<ElementalStats>(data.Slice(0, 64));
}
private static ElementalStats ReadElementalStatsNew(ref Span<byte> 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<ElementalStats> MemoryMarshalCastArray()
{
var data = _arrayData.AsSpan();
var elements = MemoryMarshal.Cast<byte, ElementalStats>(data);
return elements;
}
[Benchmark]
public Span<ElementalStats> MemoryMarshalReadArray()
{
Span<ElementalStats> 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<ElementalStats> ManualSpanSlicingArray()
{
Span<ElementalStats> 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;
}
}