From 272c3f8e84c061ce87f8aa134d388209c4822086 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Thu, 22 Dec 2022 21:39:58 +0100 Subject: [PATCH] Add archetype component access benchmark (#3606) --- .../ArchetypeComponentAccessBenchmark.cs | 372 ++++++++++++++++++ Robust.Benchmarks/Program.cs | 7 +- 2 files changed, 376 insertions(+), 3 deletions(-) create mode 100644 Robust.Benchmarks/EntityManager/ArchetypeComponentAccessBenchmark.cs diff --git a/Robust.Benchmarks/EntityManager/ArchetypeComponentAccessBenchmark.cs b/Robust.Benchmarks/EntityManager/ArchetypeComponentAccessBenchmark.cs new file mode 100644 index 000000000..dee535464 --- /dev/null +++ b/Robust.Benchmarks/EntityManager/ArchetypeComponentAccessBenchmark.cs @@ -0,0 +1,372 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using Robust.Shared.Analyzers; + +namespace Robust.Benchmarks.EntityManager; + +[MemoryDiagnoser] +[Virtual] +public class ArchetypeComponentAccessBenchmark +{ + private const int N = 10000; + + private Dictionary> _componentDictionary = default!; + private Archetype _archetype = default!; + + [GlobalSetup] + public void GlobalSetup() + { + _componentDictionary = new Dictionary>(N) + { + [typeof(Type1)] = new(), + [typeof(Type2)] = new(), + [typeof(Type3)] = new(), + [typeof(Type4)] = new(), + [typeof(Type5)] = new(), + [typeof(Type6)] = new(), + [typeof(Type7)] = new(), + [typeof(Type8)] = new(), + [typeof(Type9)] = new(), + [typeof(Type10)] = new(), + }; + _archetype = new Archetype(N); + + for (var i = 0; i < N; i++) + { + _componentDictionary[typeof(Type1)][i] = new Type1(); + _componentDictionary[typeof(Type2)][i] = new Type2(); + _componentDictionary[typeof(Type3)][i] = new Type3(); + _componentDictionary[typeof(Type4)][i] = new Type4(); + _componentDictionary[typeof(Type5)][i] = new Type5(); + _componentDictionary[typeof(Type6)][i] = new Type6(); + _componentDictionary[typeof(Type7)][i] = new Type7(); + _componentDictionary[typeof(Type8)][i] = new Type8(); + _componentDictionary[typeof(Type9)][i] = new Type9(); + _componentDictionary[typeof(Type10)][i] = new Type10(); + + _archetype.AddEntity(i); + _archetype.AddComponent(i, new Type1()); + _archetype.AddComponent(i, new Type2()); + _archetype.AddComponent(i, new Type3()); + _archetype.AddComponent(i, new Type4()); + _archetype.AddComponent(i, new Type5()); + _archetype.AddComponent(i, new Type6()); + _archetype.AddComponent(i, new Type7()); + _archetype.AddComponent(i, new Type8()); + _archetype.AddComponent(i, new Type9()); + _archetype.AddComponent(i, new Type10()); + } + } + + [Benchmark] + public Type1 GetSingleComponentsDictionary() + { + return (Type1) _componentDictionary[typeof(Type1)][1584]; + } + + [Benchmark] + public Type1 GetSingleComponentsArchetypeCast() + { + return _archetype.GetComponentCast(1584); + } + + [Benchmark] + public Type1 GetSingleComponentsArchetypeCastHandle() + { + // Handle is the same as the id + return _archetype.GetComponentCastHandle(1584); + } + + [Benchmark] + public Type1 GetSingleComponentsArchetypeUnsafe() + { + return _archetype.GetComponentUnsafe(1584); + } + + [Benchmark] + public Type1 GetSingleComponentsArchetypeUnsafeHandle() + { + // Handle is the same as the id + return _archetype.GetComponentUnsafeHandle(1584); + } + + [Benchmark] + public (Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, Type10) GetTenComponentsDictionary() + { + return ( + (Type1) _componentDictionary[typeof(Type1)][1584], + (Type2) _componentDictionary[typeof(Type2)][1584], + (Type3) _componentDictionary[typeof(Type3)][1584], + (Type4) _componentDictionary[typeof(Type4)][1584], + (Type5) _componentDictionary[typeof(Type5)][1584], + (Type6) _componentDictionary[typeof(Type6)][1584], + (Type7) _componentDictionary[typeof(Type7)][1584], + (Type8) _componentDictionary[typeof(Type8)][1584], + (Type9) _componentDictionary[typeof(Type9)][1584], + (Type10) _componentDictionary[typeof(Type10)][1584] + ); + } + + [Benchmark] + public (Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, Type10) GetTenComponentsArchetypeCast() + { + return ( + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584), + _archetype.GetComponentCast(1584) + ); + } + + [Benchmark] + public (Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, Type10) GetTenComponentsArchetypeCastHandle() + { + // Handle is the same as the id + return ( + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584), + _archetype.GetComponentCastHandle(1584) + ); + } + + [Benchmark] + public (Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, Type10) GetTenComponentsArchetypeUnsafe() + { + return ( + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584), + _archetype.GetComponentUnsafe(1584) + ); + } + + [Benchmark] + public (Type1, Type2, Type3, Type4, Type5, Type6, Type7, Type8, Type9, Type10) GetTenComponentsArchetypeUnsafeHandle() + { + // Handle is the same as the id + return ( + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584), + _archetype.GetComponentUnsafeHandle(1584) + ); + } + + // Just a bunch of types to pad the size of the arrays and such. + + // @formatter:off + // ReSharper disable UnusedType.Local + public struct Type1{} + public struct Type2{} + public struct Type3{} + public struct Type4{} + public struct Type5{} + public struct Type6{} + public struct Type7{} + public struct Type8{} + public struct Type9{} + public struct Type10{} + // ReSharper restore UnusedType.Local + // @formatter:on + + private class Archetype + { + private int _nextId; + private readonly Dictionary _ids; + private readonly T1[] _t1Comps; + private readonly T2[] _t2Comps; + private readonly T3[] _t3Comps; + private readonly T4[] _t4Comps; + private readonly T5[] _t5Comps; + private readonly T6[] _t6Comps; + private readonly T7[] _t7Comps; + private readonly T8[] _t8Comps; + private readonly T9[] _t9Comps; + private readonly T10[] _t10Comps; + + public Archetype(int n) + { + _ids = new Dictionary(n); + _t1Comps = new T1[n]; + _t2Comps = new T2[n]; + _t3Comps = new T3[n]; + _t4Comps = new T4[n]; + _t5Comps = new T5[n]; + _t6Comps = new T6[n]; + _t7Comps = new T7[n]; + _t8Comps = new T8[n]; + _t9Comps = new T9[n]; + _t10Comps = new T10[n]; + } + + public void AddEntity(int entity) + { + var id = _nextId++; + _ids[entity] = id; + } + + public void AddComponent(int entity, T val) + { + var id = _ids[entity]; + + switch (val) + { + case T1 val1: + _t1Comps[id] = val1; + break; + case T2 val2: + _t2Comps[id] = val2; + break; + case T3 val3: + _t3Comps[id] = val3; + break; + case T4 val4: + _t4Comps[id] = val4; + break; + case T5 val5: + _t5Comps[id] = val5; + break; + case T6 val6: + _t6Comps[id] = val6; + break; + case T7 val7: + _t7Comps[id] = val7; + break; + case T8 val8: + _t8Comps[id] = val8; + break; + case T9 val9: + _t9Comps[id] = val9; + break; + case T10 val10: + _t10Comps[id] = val10; + break; + } + } + + public T GetComponentCast(int entity, T val = default) where T : struct + { + var id = _ids[entity]; + + return val switch + { + T1 => (T) (object) _t1Comps[id]!, + T2 => (T) (object) _t2Comps[id]!, + T3 => (T) (object) _t3Comps[id]!, + T4 => (T) (object) _t4Comps[id]!, + T5 => (T) (object) _t5Comps[id]!, + T6 => (T) (object) _t6Comps[id]!, + T7 => (T) (object) _t7Comps[id]!, + T8 => (T) (object) _t8Comps[id]!, + T9 => (T) (object) _t9Comps[id]!, + T10 => (T) (object) _t10Comps[id]!, + _ => throw new ArgumentException($"Unknown type: {typeof(T)}") + }; + } + + public T GetComponentCastHandle(int handle, T val = default) where T : struct + { + return val switch + { + T1 => (T) (object) _t1Comps[handle]!, + T2 => (T) (object) _t2Comps[handle]!, + T3 => (T) (object) _t3Comps[handle]!, + T4 => (T) (object) _t4Comps[handle]!, + T5 => (T) (object) _t5Comps[handle]!, + T6 => (T) (object) _t6Comps[handle]!, + T7 => (T) (object) _t7Comps[handle]!, + T8 => (T) (object) _t8Comps[handle]!, + T9 => (T) (object) _t9Comps[handle]!, + T10 => (T) (object) _t10Comps[handle]!, + _ => throw new ArgumentException($"Unknown type: {typeof(T)}") + }; + } + + public ref T GetComponentUnsafe(int entity, T val = default) where T : struct + { + var id = _ids[entity]; + + switch (val) + { + case T1: + return ref Unsafe.As(ref _t1Comps[id]); + case T2: + return ref Unsafe.As(ref _t2Comps[id]); + case T3: + return ref Unsafe.As(ref _t3Comps[id]); + case T4: + return ref Unsafe.As(ref _t4Comps[id]); + case T5: + return ref Unsafe.As(ref _t5Comps[id]); + case T6: + return ref Unsafe.As(ref _t6Comps[id]); + case T7: + return ref Unsafe.As(ref _t7Comps[id]); + case T8: + return ref Unsafe.As(ref _t8Comps[id]); + case T9: + return ref Unsafe.As(ref _t9Comps[id]); + case T10: + return ref Unsafe.As(ref _t10Comps[id]); + default: + throw new ArgumentException($"Unknown type: {typeof(T)}"); + } + } + + public ref T GetComponentUnsafeHandle(int handle, T val = default) where T : struct + { + switch (val) + { + case T1: + return ref Unsafe.As(ref _t1Comps[handle]); + case T2: + return ref Unsafe.As(ref _t2Comps[handle]); + case T3: + return ref Unsafe.As(ref _t3Comps[handle]); + case T4: + return ref Unsafe.As(ref _t4Comps[handle]); + case T5: + return ref Unsafe.As(ref _t5Comps[handle]); + case T6: + return ref Unsafe.As(ref _t6Comps[handle]); + case T7: + return ref Unsafe.As(ref _t7Comps[handle]); + case T8: + return ref Unsafe.As(ref _t8Comps[handle]); + case T9: + return ref Unsafe.As(ref _t9Comps[handle]); + case T10: + return ref Unsafe.As(ref _t10Comps[handle]); + default: + throw new ArgumentException($"Unknown type: {typeof(T)}"); + } + } + } +} diff --git a/Robust.Benchmarks/Program.cs b/Robust.Benchmarks/Program.cs index 458f50494..202328591 100644 --- a/Robust.Benchmarks/Program.cs +++ b/Robust.Benchmarks/Program.cs @@ -1,8 +1,9 @@ -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Running; +#if DEBUG +using BenchmarkDotNet.Configs; +#endif using System; +using BenchmarkDotNet.Running; using Robust.Benchmarks.Configs; -using Robust.Benchmarks.Exporters; namespace Robust.Benchmarks {