EntityFetchBenchmark.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. using System;
  2. using System.Collections.Generic;
  3. using BenchmarkDotNet.Attributes;
  4. using Robust.Shared.Analyzers;
  5. using Robust.Shared.Utility;
  6. namespace Content.Benchmarks
  7. {
  8. [SimpleJob]
  9. [Virtual]
  10. public class EntityFetchBenchmark
  11. {
  12. [Params(1000)] public int N { get; set; }
  13. public int M { get; set; } = 10;
  14. private readonly DictEntityStorage _dictStorage = new();
  15. private readonly GenEntityStorage _genStorage = new();
  16. private IEntityStorage<DictEntity, DictEntityUid> _dictStorageInterface;
  17. private IEntityStorage<GenEntity, GenEntityUid> _genStorageInterface;
  18. private DictEntityUid[] _toReadDict;
  19. private DictEntity[] _toWriteDict;
  20. private GenEntityUid[] _toReadGen;
  21. private GenEntity[] _toWriteGen;
  22. [GlobalSetup]
  23. public void Setup()
  24. {
  25. _dictStorageInterface = _dictStorage;
  26. _genStorageInterface = _genStorage;
  27. var r = new Random();
  28. var allocatedGen = new List<GenEntity>();
  29. var allocatedDict = new List<DictEntity>();
  30. for (var i = 0; i < N; i++)
  31. {
  32. allocatedGen.Add(_genStorage.NewEntity());
  33. allocatedDict.Add(_dictStorage.NewEntity());
  34. }
  35. var delTo = N / 2;
  36. for (var i = 0; i < delTo; i++)
  37. {
  38. var index = r.Next(allocatedDict.Count);
  39. var gEnt = allocatedGen[index];
  40. var dEnt = allocatedDict[index];
  41. _genStorage.DeleteEntity(gEnt);
  42. _dictStorage.DeleteEntity(dEnt);
  43. allocatedGen.RemoveSwap(i);
  44. allocatedDict.RemoveSwap(i);
  45. }
  46. for (var i = 0; i < N; i++)
  47. {
  48. allocatedGen.Add(_genStorage.NewEntity());
  49. allocatedDict.Add(_dictStorage.NewEntity());
  50. }
  51. for (var i = 0; i < delTo; i++)
  52. {
  53. var index = r.Next(allocatedDict.Count);
  54. var gEnt = allocatedGen[index];
  55. var dEnt = allocatedDict[index];
  56. _genStorage.DeleteEntity(gEnt);
  57. _dictStorage.DeleteEntity(dEnt);
  58. allocatedGen.RemoveSwap(i);
  59. allocatedDict.RemoveSwap(i);
  60. }
  61. _toReadDict = new DictEntityUid[M];
  62. _toWriteDict = new DictEntity[M];
  63. _toReadGen = new GenEntityUid[M];
  64. _toWriteGen = new GenEntity[M];
  65. for (var i = 0; i < M; i++)
  66. {
  67. var index = r.Next(allocatedDict.Count);
  68. _toReadDict[i] = allocatedDict[index].Uid;
  69. _toReadGen[i] = allocatedGen[index].Uid;
  70. }
  71. }
  72. [Benchmark]
  73. public void BenchGenId()
  74. {
  75. for (var i = 0; i < M; i++)
  76. {
  77. var uid = _toReadGen[i];
  78. if (_genStorage.TryGetEntity(uid, out var entity))
  79. {
  80. _toWriteGen[i] = entity;
  81. }
  82. }
  83. }
  84. [Benchmark]
  85. public void BenchDict()
  86. {
  87. for (var i = 0; i < M; i++)
  88. {
  89. var uid = _toReadDict[i];
  90. if (_dictStorage.TryGetEntity(uid, out var entity))
  91. {
  92. _toWriteDict[i] = entity;
  93. }
  94. }
  95. }
  96. [Benchmark]
  97. public void BenchGenIdInterface()
  98. {
  99. for (var i = 0; i < M; i++)
  100. {
  101. var uid = _toReadGen[i];
  102. if (_genStorageInterface.TryGetEntity(uid, out var entity))
  103. {
  104. _toWriteGen[i] = entity;
  105. }
  106. }
  107. }
  108. [Benchmark]
  109. public void BenchDictInterface()
  110. {
  111. for (var i = 0; i < M; i++)
  112. {
  113. var uid = _toReadDict[i];
  114. if (_dictStorageInterface.TryGetEntity(uid, out var entity))
  115. {
  116. _toWriteDict[i] = entity;
  117. }
  118. }
  119. }
  120. private sealed class DictEntityStorage : EntityStorage<DictEntity, DictEntityUid>
  121. {
  122. private int _nextValue;
  123. private readonly Dictionary<DictEntityUid, DictEntity> _dict = new();
  124. public override bool TryGetEntity(DictEntityUid entityUid, out DictEntity entity)
  125. {
  126. if (!_dict.TryGetValue(entityUid, out entity))
  127. {
  128. return false;
  129. }
  130. return !entity.Deleted;
  131. }
  132. public DictEntity NewEntity()
  133. {
  134. var e = new DictEntity(new DictEntityUid(_nextValue++));
  135. _dict.Add(e.Uid, e);
  136. return e;
  137. }
  138. public void DeleteEntity(DictEntity e)
  139. {
  140. DebugTools.Assert(!e.Deleted);
  141. e.Deleted = true;
  142. _dict.Remove(e.Uid);
  143. }
  144. }
  145. private interface IEntityStorage<TEntity, TEntityUid>
  146. {
  147. public bool TryGetEntity(TEntityUid entityUid, out TEntity entity);
  148. }
  149. private abstract class EntityStorage<TEntity, TEntityUid> : IEntityStorage<TEntity, TEntityUid>
  150. {
  151. public abstract bool TryGetEntity(TEntityUid entityUid, out TEntity entity);
  152. public TEntity GetEntity(TEntityUid entityUid)
  153. {
  154. if (!TryGetEntity(entityUid, out var entity))
  155. throw new ArgumentException($"Failed to get entity {entityUid} from storage.");
  156. return entity;
  157. }
  158. }
  159. private sealed class GenEntityStorage : EntityStorage<GenEntity, GenEntityUid>
  160. {
  161. private (int generation, GenEntity entity)[] _entities = new (int, GenEntity)[1];
  162. private readonly List<int> _availableSlots = new() { 0 };
  163. public override bool TryGetEntity(GenEntityUid entityUid, out GenEntity entity)
  164. {
  165. var (generation, genEntity) = _entities[entityUid.Index];
  166. entity = genEntity;
  167. return generation == entityUid.Generation;
  168. }
  169. public GenEntity NewEntity()
  170. {
  171. if (_availableSlots.Count == 0)
  172. {
  173. // Reallocate
  174. var oldEntities = _entities;
  175. _entities = new (int, GenEntity)[_entities.Length * 2];
  176. oldEntities.CopyTo(_entities, 0);
  177. for (var i = oldEntities.Length; i < _entities.Length; i++)
  178. {
  179. _availableSlots.Add(i);
  180. }
  181. }
  182. var index = _availableSlots.Pop();
  183. ref var slot = ref _entities[index];
  184. var slotEntity = new GenEntity(new GenEntityUid(slot.generation, index));
  185. slot.entity = slotEntity;
  186. return slotEntity;
  187. }
  188. public void DeleteEntity(GenEntity e)
  189. {
  190. DebugTools.Assert(!e.Deleted);
  191. e.Deleted = true;
  192. ref var slot = ref _entities[e.Uid.Index];
  193. slot.entity = null;
  194. slot.generation += 1;
  195. _availableSlots.Add(e.Uid.Index);
  196. }
  197. }
  198. private readonly struct DictEntityUid : IEquatable<DictEntityUid>
  199. {
  200. public readonly int Value;
  201. public DictEntityUid(int value)
  202. {
  203. Value = value;
  204. }
  205. public bool Equals(DictEntityUid other)
  206. {
  207. return Value == other.Value;
  208. }
  209. public override bool Equals(object obj)
  210. {
  211. return obj is DictEntityUid other && Equals(other);
  212. }
  213. public override int GetHashCode()
  214. {
  215. return Value;
  216. }
  217. public static bool operator ==(DictEntityUid left, DictEntityUid right)
  218. {
  219. return left.Equals(right);
  220. }
  221. public static bool operator !=(DictEntityUid left, DictEntityUid right)
  222. {
  223. return !left.Equals(right);
  224. }
  225. }
  226. private readonly struct GenEntityUid
  227. {
  228. public readonly int Generation;
  229. public readonly int Index;
  230. public GenEntityUid(int generation, int index)
  231. {
  232. Generation = generation;
  233. Index = index;
  234. }
  235. }
  236. private sealed class DictEntity
  237. {
  238. public DictEntity(DictEntityUid uid)
  239. {
  240. Uid = uid;
  241. }
  242. public DictEntityUid Uid { get; }
  243. public bool Deleted { get; set; }
  244. }
  245. private sealed class GenEntity
  246. {
  247. public GenEntityUid Uid { get; }
  248. public bool Deleted { get; set; }
  249. public GenEntity(GenEntityUid uid)
  250. {
  251. Uid = uid;
  252. }
  253. }
  254. }
  255. }