* Content.Client * Content.Benchmarks * Content.IntegrationTests * Content.Server * Content.Server.Database * Content.Shared * Content.Tests * Merge fixes Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
258 lines
7.5 KiB
C#
258 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BenchmarkDotNet.Attributes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Benchmarks
|
|
{
|
|
[SimpleJob]
|
|
public class ComponentFetchBenchmark
|
|
{
|
|
[Params(5000)] public int NEnt { get; set; }
|
|
|
|
private readonly Dictionary<(EntityUid, Type), BComponent>
|
|
_componentsFlat = new();
|
|
|
|
private readonly Dictionary<Type, Dictionary<EntityUid, BComponent>> _componentsPart =
|
|
new();
|
|
|
|
private UniqueIndex<Type, BComponent> _allComponents = new();
|
|
|
|
private readonly List<EntityUid> _lookupEntities = new();
|
|
|
|
[GlobalSetup]
|
|
public void Setup()
|
|
{
|
|
var random = new Random();
|
|
|
|
_componentsPart[typeof(BComponent1)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent2)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent3)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent4)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponentLookup)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent6)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent7)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent8)] = new Dictionary<EntityUid, BComponent>();
|
|
_componentsPart[typeof(BComponent9)] = new Dictionary<EntityUid, BComponent>();
|
|
|
|
for (var i = 0u; i < NEnt; i++)
|
|
{
|
|
var eId = new EntityUid(i);
|
|
|
|
if (random.Next(1) == 0)
|
|
{
|
|
_lookupEntities.Add(eId);
|
|
}
|
|
|
|
var comps = new List<BComponent>
|
|
{
|
|
new BComponent1(),
|
|
new BComponent2(),
|
|
new BComponent3(),
|
|
new BComponent4(),
|
|
new BComponent6(),
|
|
new BComponent7(),
|
|
new BComponent8(),
|
|
new BComponent9(),
|
|
};
|
|
|
|
if (random.Next(1000) == 0)
|
|
{
|
|
comps.Add(new BComponentLookup());
|
|
}
|
|
|
|
foreach (var comp in comps)
|
|
{
|
|
comp.Uid = eId;
|
|
var type = comp.GetType();
|
|
_componentsPart[type][eId] = comp;
|
|
_componentsFlat[(eId, type)] = comp;
|
|
_allComponents.Add(type, comp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// These two benchmarks are find "needles in haystack" components.
|
|
// We try to look up a component that 0.1% of entities have on 1% of entities.
|
|
// Examples of this in the engine are VisibilityComponent lookups during PVS.
|
|
[Benchmark]
|
|
public void FindPart()
|
|
{
|
|
foreach (var entityUid in _lookupEntities)
|
|
{
|
|
var d = _componentsPart[typeof(BComponentLookup)];
|
|
d.TryGetValue(entityUid, out _);
|
|
}
|
|
}
|
|
|
|
[Benchmark]
|
|
public void FindFlat()
|
|
{
|
|
foreach (var entityUid in _lookupEntities)
|
|
{
|
|
_componentsFlat.TryGetValue((entityUid, typeof(BComponentLookup)), out _);
|
|
}
|
|
}
|
|
|
|
// Iteration benchmarks:
|
|
// We try to iterate every instance of a single component (BComponent1) and see which is faster.
|
|
[Benchmark]
|
|
public void IterPart()
|
|
{
|
|
var list = _componentsPart[typeof(BComponent1)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list.Values)
|
|
{
|
|
arr[i++] = c;
|
|
}
|
|
}
|
|
|
|
[Benchmark]
|
|
public void IterFlat()
|
|
{
|
|
var list = _allComponents[typeof(BComponent1)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list)
|
|
{
|
|
arr[i++] = c;
|
|
}
|
|
}
|
|
|
|
// We do the same as the iteration benchmarks but re-fetch the component every iteration.
|
|
// This is what entity systems mostly do via entity queries because crappy code.
|
|
[Benchmark]
|
|
public void IterFetchPart()
|
|
{
|
|
var list = _componentsPart[typeof(BComponent1)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list.Values)
|
|
{
|
|
var eId = c.Uid;
|
|
var d = _componentsPart[typeof(BComponent1)];
|
|
arr[i++] = d[eId];
|
|
}
|
|
}
|
|
|
|
[Benchmark]
|
|
public void IterFetchFlat()
|
|
{
|
|
var list = _allComponents[typeof(BComponent1)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list)
|
|
{
|
|
var eId = c.Uid;
|
|
arr[i++] = _componentsFlat[(eId, typeof(BComponent1))];
|
|
}
|
|
}
|
|
|
|
// Same as the previous benchmarks but with BComponentLookup instead.
|
|
// Which is only on 1% of entities.
|
|
[Benchmark]
|
|
public void IterFetchPartRare()
|
|
{
|
|
var list = _componentsPart[typeof(BComponentLookup)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list.Values)
|
|
{
|
|
var eId = c.Uid;
|
|
var d = _componentsPart[typeof(BComponentLookup)];
|
|
arr[i++] = d[eId];
|
|
}
|
|
}
|
|
|
|
[Benchmark]
|
|
public void IterFetchFlatRare()
|
|
{
|
|
var list = _allComponents[typeof(BComponentLookup)];
|
|
var arr = new BComponent[list.Count];
|
|
var i = 0;
|
|
foreach (var c in list)
|
|
{
|
|
var eId = c.Uid;
|
|
arr[i++] = _componentsFlat[(eId, typeof(BComponentLookup))];
|
|
}
|
|
}
|
|
|
|
private readonly struct EntityUid : IEquatable<EntityUid>
|
|
{
|
|
public readonly uint Value;
|
|
|
|
public EntityUid(uint value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public bool Equals(EntityUid other)
|
|
{
|
|
return Value == other.Value;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is EntityUid other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return (int) Value;
|
|
}
|
|
|
|
public static bool operator ==(EntityUid left, EntityUid right)
|
|
{
|
|
return left.Equals(right);
|
|
}
|
|
|
|
public static bool operator !=(EntityUid left, EntityUid right)
|
|
{
|
|
return !left.Equals(right);
|
|
}
|
|
}
|
|
|
|
private abstract class BComponent
|
|
{
|
|
public EntityUid Uid;
|
|
}
|
|
|
|
private class BComponent1 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent2 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent3 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent4 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponentLookup : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent6 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent7 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent8 : BComponent
|
|
{
|
|
}
|
|
|
|
private class BComponent9 : BComponent
|
|
{
|
|
}
|
|
}
|
|
}
|