diff --git a/src/Arch.Benchmarks/Benchmark.cs b/src/Arch.Benchmarks/Benchmark.cs index eec4e08..5119809 100644 --- a/src/Arch.Benchmarks/Benchmark.cs +++ b/src/Arch.Benchmarks/Benchmark.cs @@ -9,7 +9,6 @@ public class Benchmark { private static void Main(string[] args) { - /* // NOTE: Can this be replaced with ManualConfig.CreateEmpty()? #pragma warning disable HAA0101 // Array allocation for params parameter var config = new ManualConfig() @@ -18,28 +17,9 @@ private static void Main(string[] args) .AddLogger(ConsoleLogger.Default) .AddColumnProvider(DefaultColumnProviders.Instance); #pragma warning restore HAA0101 // Array allocation for params parameter - */ - - - - var world = World.Create(); - for (var index = 0; index <= 100; index++) - { - world.Create(); - } - - var desc = new QueryDescription().WithAll(); - for (var index = 0; index <= 100000; index++) - { - world.Query(in desc, (ref int i) => - { - }); - } - - // NOTE: Is `-- --job` a typo? // Use: dotnet run -c Release --framework net7.0 -- --job short --filter *IterationBenchmark* - //BenchmarkSwitcher.FromAssembly(typeof(Benchmark).Assembly).Run(args, config); + BenchmarkSwitcher.FromAssembly(typeof(Benchmark).Assembly).Run(args, config); } } diff --git a/src/Arch.Benchmarks/TryGetBenchmark.cs b/src/Arch.Benchmarks/TryGetBenchmark.cs new file mode 100644 index 0000000..a88a71a --- /dev/null +++ b/src/Arch.Benchmarks/TryGetBenchmark.cs @@ -0,0 +1,70 @@ +using Arch.Core; +using Arch.Core.Utils; + +namespace Arch.Benchmarks; + +[HtmlExporter] +[MemoryDiagnoser] +[HardwareCounters(HardwareCounter.CacheMisses)] +public class TryGetBenchmark +{ + private static World _world; + private static List _entities; + + private Consumer _consumer = new(); + + [GlobalSetup] + public void Setup() + { + _world = World.Create(); + + _entities = new List(1_000_000); + for (var index = 0; index < 1_000_000; index++) + { + _entities.Add(_world.Create(new Transform(), new Velocity())); + } + } + + [Benchmark] + public void TryGetGenericRef() + { + for (var index = 0; index < _entities.Count; index++) + { + var entity = _entities[index]; + var xform = _world.TryGetRef(entity, out var exists); + + if (exists) + { + _consumer.Consume(xform); + } + } + } + + [Benchmark] + public void TryGetGeneric() + { + for (var index = 0; index < _entities.Count; index++) + { + var entity = _entities[index]; + + if (_world.TryGet(entity, out var xform)) + { + _consumer.Consume(xform); + } + } + } + + [Benchmark] + public void TryGet() + { + for (var index = 0; index < _entities.Count; index++) + { + var entity = _entities[index]; + + if (_world.TryGet(entity, Component.GetComponentType(typeof(Transform)), out var xform)) + { + _consumer.Consume(xform); + } + } + } +} diff --git a/src/Arch.Tests/WorldTest.cs b/src/Arch.Tests/WorldTest.cs index d219795..e49f6a9 100644 --- a/src/Arch.Tests/WorldTest.cs +++ b/src/Arch.Tests/WorldTest.cs @@ -604,6 +604,18 @@ public void Add() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(arch, Is.EqualTo(_world.GetArchetype(entity))); } + + /// + /// Checks if generic TryGet works on entities. + /// + [Test] + public void TryGet() + { + var entity = _world.Create(new Transform()); + + That(_world.TryGet(entity, out Transform xform), Is.EqualTo(true)); + That(_world.TryGet(entity, out Rotation rot), Is.EqualTo(false)); + } } @@ -658,6 +670,18 @@ public void Add_NonGeneric() That(_world.GetArchetype(entity2), Is.EqualTo(_world.GetArchetype(entity))); That(arch, Is.EqualTo(_world.GetArchetype(entity))); } + + /// + /// Checks if generic TryGet works on entities. + /// + [Test] + public void TryGet_NonGeneric() + { + var entity = _world.Create(new Transform()); + + That(_world.TryGet(entity, Component.ComponentType, out var xform), Is.EqualTo(true)); + That(_world.TryGet(entity, Component.ComponentType, out var rot), Is.EqualTo(false)); + } } /// diff --git a/src/Arch/Core/Archetype.cs b/src/Arch/Core/Archetype.cs index c6a0519..12fe600 100644 --- a/src/Arch/Core/Archetype.cs +++ b/src/Arch/Core/Archetype.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Diagnostics.Contracts; using Arch.Core.Extensions; using Arch.Core.Extensions.Internal; using Arch.Core.Utils; @@ -299,6 +300,27 @@ internal Archetype(Signature signature) _removeEdges = new SparseJaggedArray(BucketSize); } + /// + /// Try get the index of a component within this archetype. Returns false if the archetype does not have this + /// component. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Pure] + internal bool TryIndex(out int i) + { + var id = Component.ComponentType.Id; + Debug.Assert(id != -1, $"Index is out of bounds, component {typeof(T)} with id {id} does not exist in this chunk."); + + if (id >= _componentIdToArrayIndex.Length) + { + i = -1; + return false; + } + + i = _componentIdToArrayIndex.DangerousGetReferenceAt(id); + return i != -1; + } + /// /// The component types that the 's stored here have. /// diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index a200b4a..d0f84a3 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -4,6 +4,7 @@ using Arch.Core.Extensions.Internal; using Arch.Core.Utils; using Collections.Pooled; +using CommunityToolkit.HighPerformance; using Schedulers; using Component = Arch.Core.Utils.Component; @@ -983,18 +984,18 @@ public ref T Get(Entity entity) [Pure] public bool TryGet(Entity entity, out T? component) { - component = default; - - var entitySlot = EntityInfo.GetEntitySlot(entity.Id); - var slot = entitySlot.Slot; - var archetype = entitySlot.Archetype; + ref var slot = ref EntityInfo.EntitySlots[entity.Id]; - if (!archetype.Has()) + if (!slot.Archetype.TryIndex(out int compIndex)) { + component = default; return false; } - component = archetype.Get(ref slot); + ref var chunk = ref slot.Archetype.GetChunk(slot.Slot.ChunkIndex); + Debug.Assert(compIndex != -1 && compIndex < chunk.Components.Length, $"Index is out of bounds, component {typeof(T)} with id {compIndex} does not exist in this chunk."); + var array = Unsafe.As(chunk.Components.DangerousGetReferenceAt(compIndex)); + component = array[slot.Slot.Index]; return true; } @@ -1009,16 +1010,14 @@ public bool TryGet(Entity entity, out T? component) [Pure] public ref T TryGetRef(Entity entity, out bool exists) { - var entitySlot = EntityInfo.GetEntitySlot(entity.Id); - var slot = entitySlot.Slot; - var archetype = entitySlot.Archetype; + ref var slot = ref EntityInfo.EntitySlots[entity.Id]; - if (!(exists = archetype.Has())) + if (!(exists = slot.Archetype.Has())) { return ref Unsafe.NullRef(); } - return ref archetype.Get(ref slot); + return ref slot.Archetype.Get(ref slot.Slot); } ///