Skip to content

Commit 13e140c

Browse files
Re-use slot for Add / Remove ops (#266)
Optimisation.
1 parent 4a1b222 commit 13e140c

File tree

6 files changed

+347
-259
lines changed

6 files changed

+347
-259
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Arch.Core;
2+
3+
namespace Arch.Benchmarks;
4+
5+
[HtmlExporter]
6+
[MemoryDiagnoser]
7+
public class AddRemoveBenchmark
8+
{
9+
[Params(10000, 100000, 1000000)] public int Amount;
10+
11+
private static World _world;
12+
private static List<Entity> _entities;
13+
14+
[GlobalSetup]
15+
public void Setup()
16+
{
17+
_world = World.Create();
18+
19+
_entities = new List<Entity>(Amount);
20+
for (var index = 0; index < Amount; index++)
21+
{
22+
_entities.Add(_world.Create(new Transform(), new Velocity()));
23+
}
24+
}
25+
26+
[Benchmark]
27+
public void Add()
28+
{
29+
for (var index = 0; index < _entities.Count; index++)
30+
{
31+
var entity = _entities[index];
32+
_world.Add<Position2D>(entity);
33+
}
34+
}
35+
36+
[Benchmark]
37+
public void Remove()
38+
{
39+
for (var index = 0; index < _entities.Count; index++)
40+
{
41+
var entity = _entities[index];
42+
_world.Remove<Transform>(entity);
43+
}
44+
}
45+
}

src/Arch.Benchmarks/Benchmark.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class Benchmark
99
{
1010
private static void Main(string[] args)
1111
{
12-
/*
12+
1313
// NOTE: Can this be replaced with ManualConfig.CreateEmpty()?
1414
#pragma warning disable HAA0101 // Array allocation for params parameter
1515
var config = new ManualConfig()
@@ -18,30 +18,9 @@ private static void Main(string[] args)
1818
.AddLogger(ConsoleLogger.Default)
1919
.AddColumnProvider(DefaultColumnProviders.Instance);
2020
#pragma warning restore HAA0101 // Array allocation for params parameter
21-
*/
22-
23-
24-
var world = World.Create();
25-
world.Create(1_000_00, 10);
26-
/*world.Reserve(in Component<int>.Signature, 1_000_00);
27-
for (var index = 0; index <= 1_000_00; index++)
28-
{
29-
world.Create<int>();
30-
}*/
31-
32-
/*
33-
var desc = new QueryDescription().WithAll<int>();
34-
for (var index = 0; index <= 100000; index++)
35-
{
36-
world.Query(in desc, (ref int i) =>
37-
{
38-
});
39-
}*/
40-
41-
4221

4322
// NOTE: Is `-- --job` a typo?
4423
// Use: dotnet run -c Release --framework net7.0 -- --job short --filter *IterationBenchmark*
45-
//BenchmarkSwitcher.FromAssembly(typeof(Benchmark).Assembly).Run(args, config);
24+
BenchmarkSwitcher.FromAssembly(typeof(Benchmark).Assembly).Run(args, config);
4625
}
4726
}

src/Arch/Buffer/CommandBuffer.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,15 +436,16 @@ public void Dispose()
436436
public sealed partial class CommandBuffer
437437
{
438438
/// <summary>
439-
/// Adds an list of new components to the <see cref="Entity"/> and moves it to the new <see cref="Archetype"/>.
439+
/// Adds a list of new components to the <see cref="Entity"/> and moves it to the new <see cref="Archetype"/>.
440440
/// </summary>
441441
/// <param name="world">The world to operate on.</param>
442442
/// <param name="entity">The <see cref="Entity"/>.</param>
443443
/// <param name="components">A <see cref="IList{T}"/> of <see cref="ComponentType"/>'s, those are added to the <see cref="Entity"/>.</param>
444444
[SkipLocalsInit]
445445
internal static void AddRange(World world, Entity entity, Span<ComponentType> components)
446446
{
447-
var oldArchetype = world.EntityInfo.GetArchetype(entity.Id);
447+
ref var data = ref world.EntityInfo.EntityData[entity.Id];
448+
var oldArchetype = data.Archetype;
448449

449450
// BitSet to stack/span bitset, size big enough to contain ALL registered components.
450451
Span<uint> stack = stackalloc uint[BitSet.RequiredLength(ComponentRegistry.Size)];
@@ -465,6 +466,6 @@ internal static void AddRange(World world, Entity entity, Span<ComponentType> co
465466
newArchetype = world.GetOrCreate(newSignature);
466467
}
467468

468-
world.Move(entity, oldArchetype, newArchetype, out _);
469+
world.Move(entity, ref data, oldArchetype, newArchetype, out _);
469470
}
470471
}

src/Arch/Core/World.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -317,23 +317,29 @@ public Entity Create(in Signature types)
317317
/// Moves an <see cref="Entity"/> from one <see cref="Archetype"/> <see cref="Slot"/> to another.
318318
/// </summary>
319319
/// <param name="entity">The <see cref="Entity"/>.</param>
320+
/// <param name="data">The <see cref="EntityData"/> of the supplied entity.</param>
320321
/// <param name="source">Its <see cref="Archetype"/>.</param>
321322
/// <param name="destination">The new <see cref="Archetype"/>.</param>
322323
/// <param name="destinationSlot">The new <see cref="Slot"/> in which the moved <see cref="Entity"/> will land.</param>
323-
internal void Move(Entity entity, Archetype source, Archetype destination, out Slot destinationSlot)
324+
internal void Move(Entity entity, ref EntityData data, Archetype source, Archetype destination, out Slot destinationSlot)
324325
{
326+
// Entity should match the supplied EntityData.
327+
Debug.Assert(entity == data.Archetype.Entity(ref data.Slot));
328+
325329
// A common mistake, happening in many cases.
326330
Debug.Assert(source != destination, "From-Archetype is the same as the To-Archetype. Entities cannot move within the same archetype using this function. Probably an attempt was made to attach already existing components to the entity or to remove non-existing ones.");
327331

328332
// Copy entity to other archetype
329-
ref var slot = ref EntityInfo.GetSlot(entity.Id);
333+
var slot = data.Slot;
330334
var allocatedEntities = destination.Add(entity, out _, out destinationSlot);
331335
Archetype.CopyComponents(source, ref slot, destination, ref destinationSlot);
332336
source.Remove(slot, out var movedEntity);
333337

334338
// Update moved entity from the remove
335339
EntityInfo.Move(movedEntity, slot);
336-
EntityInfo.Move(entity.Id, destination, destinationSlot);
340+
341+
data.Archetype = destination;
342+
data.Slot = destinationSlot;
337343

338344
// Calculate the entity difference between the moved archetypes to allocate more space accordingly.
339345
Capacity += allocatedEntities;
@@ -1223,11 +1229,12 @@ public ref T AddOrGet<T>(Entity entity, T? component = default)
12231229
[StructuralChange]
12241230
internal void Add<T>(Entity entity, out Archetype newArchetype, out Slot slot)
12251231
{
1226-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1232+
ref var data = ref EntityInfo.EntityData[entity.Id];
1233+
var oldArchetype = data.Archetype;
12271234
var type = Component<T>.ComponentType;
12281235
newArchetype = GetOrCreateArchetypeByAddEdge(in type, oldArchetype);
12291236

1230-
Move(entity, oldArchetype, newArchetype, out slot);
1237+
Move(entity, ref data, oldArchetype, newArchetype, out slot);
12311238
}
12321239

12331240
/// <summary>
@@ -1279,12 +1286,14 @@ public void Add<T>(Entity entity, in T component)
12791286
[StructuralChange]
12801287
public void Remove<T>(Entity entity)
12811288
{
1282-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1289+
ref var data = ref EntityInfo.EntityData[entity.Id];
1290+
var oldArchetype = data.Archetype;
1291+
12831292
var type = Component<T>.ComponentType;
12841293
var newArchetype = GetOrCreateArchetypeByRemoveEdge(in type, oldArchetype);
12851294

12861295
OnComponentRemoved<T>(entity);
1287-
Move(entity, oldArchetype, newArchetype, out _);
1296+
Move(entity, ref data, oldArchetype, newArchetype, out _);
12881297
}
12891298
}
12901299

@@ -1449,11 +1458,13 @@ public bool TryGet(Entity entity, ComponentType type, out object? component)
14491458
[StructuralChange]
14501459
public void Add(Entity entity, in object cmp)
14511460
{
1452-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1461+
ref var data = ref EntityInfo.EntityData[entity.Id];
1462+
1463+
var oldArchetype = data.Archetype;
14531464
var type = (ComponentType)cmp.GetType();
14541465
var newArchetype = GetOrCreateArchetypeByAddEdge(in type, oldArchetype);
14551466

1456-
Move(entity, oldArchetype, newArchetype, out var slot);
1467+
Move(entity, ref data, oldArchetype, newArchetype, out var slot);
14571468
newArchetype.Set(ref slot, cmp);
14581469
OnComponentAdded(entity, type);
14591470
}
@@ -1470,7 +1481,8 @@ public void Add(Entity entity, in object cmp)
14701481
[StructuralChange]
14711482
public void AddRange(Entity entity, Span<object> components)
14721483
{
1473-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1484+
ref var data = ref EntityInfo.EntityData[entity.Id];
1485+
var oldArchetype = data.Archetype;
14741486

14751487
// BitSet to stack/span bitset, size big enough to contain ALL registered components.
14761488
Span<uint> stack = stackalloc uint[BitSet.RequiredLength(ComponentRegistry.Size)];
@@ -1498,7 +1510,7 @@ public void AddRange(Entity entity, Span<object> components)
14981510
}
14991511

15001512
// Move and fire events
1501-
Move(entity, oldArchetype, newArchetype, out var slot);
1513+
Move(entity, ref data, oldArchetype, newArchetype, out var slot);
15021514
foreach (var cmp in components)
15031515
{
15041516
newArchetype.Set(ref slot, cmp);
@@ -1519,7 +1531,8 @@ public void AddRange(Entity entity, Span<object> components)
15191531
[StructuralChange]
15201532
public void AddRange(Entity entity, Span<ComponentType> components)
15211533
{
1522-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1534+
ref var data = ref EntityInfo.EntityData[entity.Id];
1535+
var oldArchetype = data.Archetype;
15231536

15241537
// BitSet to stack/span bitset, size big enough to contain ALL registered components.
15251538
Span<uint> stack = stackalloc uint[BitSet.RequiredLength(ComponentRegistry.Size)];
@@ -1540,7 +1553,7 @@ public void AddRange(Entity entity, Span<ComponentType> components)
15401553
newArchetype = GetOrCreate(newSignature);
15411554
}
15421555

1543-
Move(entity, oldArchetype, newArchetype, out _);
1556+
Move(entity, ref data, oldArchetype, newArchetype, out _);
15441557

15451558
#if EVENTS
15461559
for (var i = 0; i < components.Length; i++)
@@ -1561,7 +1574,8 @@ public void AddRange(Entity entity, Span<ComponentType> components)
15611574
[StructuralChange]
15621575
public void Remove(Entity entity, ComponentType type)
15631576
{
1564-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1577+
ref var data = ref EntityInfo.EntityData[entity.Id];
1578+
var oldArchetype = data.Archetype;
15651579

15661580
// BitSet to stack/span bitset, size big enough to contain ALL registered components.
15671581
Span<uint> stack = stackalloc uint[oldArchetype.BitSet.Length];
@@ -1578,7 +1592,7 @@ public void Remove(Entity entity, ComponentType type)
15781592
}
15791593

15801594
OnComponentRemoved(entity, type);
1581-
Move(entity, oldArchetype, newArchetype, out _);
1595+
Move(entity, ref data, oldArchetype, newArchetype, out _);
15821596
}
15831597

15841598
/// <summary>
@@ -1593,7 +1607,8 @@ public void Remove(Entity entity, ComponentType type)
15931607
[StructuralChange]
15941608
public void RemoveRange(Entity entity, Span<ComponentType> types)
15951609
{
1596-
var oldArchetype = EntityInfo.GetArchetype(entity.Id);
1610+
ref var data = ref EntityInfo.EntityData[entity.Id];
1611+
var oldArchetype = data.Archetype;
15971612

15981613
// BitSet to stack/span bitset, size big enough to contain ALL registered components.
15991614
Span<uint> stack = stackalloc uint[oldArchetype.BitSet.Length];
@@ -1620,7 +1635,7 @@ public void RemoveRange(Entity entity, Span<ComponentType> types)
16201635
OnComponentRemoved(entity, type);
16211636
}
16221637

1623-
Move(entity, oldArchetype, newArchetype, out _);
1638+
Move(entity, ref data, oldArchetype, newArchetype, out _);
16241639
}
16251640
}
16261641

0 commit comments

Comments
 (0)