continuity/Benchmarks/MemCasting.cs
Timothy Schenk 3a24dabdf2
chore: formatting and slnx
Signed-off-by: Timothy Schenk <admin@rainote.dev>
2025-01-16 14:30:40 +01:00

163 lines
5.4 KiB
C#

// 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;
}
}