163 lines
5.4 KiB
C#
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;
|
|
}
|
|
|
|
}
|