page memory usage optimization

This commit is contained in:
DCFApixels 2024-11-26 19:16:14 +08:00
parent c23e3ce621
commit d068b22d16

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -258,7 +259,8 @@ namespace DCFApixels.DragonECS
private int _count = 0; private int _count = 0;
internal bool _isReleased = true; internal bool _isReleased = true;
private static readonly int* _nullPage = UnmanagedArrayUtility.NewAndInit<int>(PageSlot.SIZE); internal static readonly int* _nullPage = UnmanagedArrayUtility.NewAndInit<int>(PageSlot.SIZE);
internal static readonly long _nullPagePtrFake = (long)_nullPage;
#region Properties #region Properties
public short WorldID public short WorldID
@ -353,15 +355,23 @@ namespace DCFApixels.DragonECS
public bool Has(int entityID) public bool Has(int entityID)
{ {
//return _sparse[entityID] != 0; //return _sparse[entityID] != 0;
PageSlot* page = _sparsePages + (entityID >> PageSlot.SHIFT); ref PageSlot page = ref _sparsePages[entityID >> PageSlot.SHIFT];
return page->Indexes[entityID & PageSlot.MASK] != 0;
//Span<PageSlot> sparse = new Span<PageSlot>(_sparsePages, _sparsePagesCount);
//
//if(page.Count == 0 && (page.Indexes[entityID & PageSlot.MASK] != 0))
//{
//
//}
return page.Count == 1 ? _dense[page.IndexesXOR] == entityID : page.Indexes[entityID & PageSlot.MASK] != 0;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IndexOf(int entityID) public int IndexOf(int entityID)
{ {
//return _sparse[entityID]; //return _sparse[entityID];
PageSlot* page = _sparsePages + (entityID >> PageSlot.SHIFT); ref PageSlot page = ref _sparsePages[entityID >> PageSlot.SHIFT];
return page->Indexes[entityID & PageSlot.MASK]; return page.Count == 1 ? page.IndexesXOR : page.Indexes[entityID & PageSlot.MASK];
} }
#endregion #endregion
@ -392,14 +402,31 @@ namespace DCFApixels.DragonECS
_dense[_count] = entityID; _dense[_count] = entityID;
//_sparse[entityID] = _count; //_sparse[entityID] = _count;
PageSlot* page = _sparsePages + (entityID >> PageSlot.SHIFT); ref PageSlot page = ref _sparsePages[entityID >> PageSlot.SHIFT];
if(page->Count == 0) if(page.Count++ == 1)
{ {
page->Indexes = _source.TakePage(); int* x = _nullPage;
if (page.Indexes != x)
{
}
page.Indexes = _source.TakePage();
} }
page->Indexes[entityID & PageSlot.MASK] = _count; if(page.Count != 1)
//page->IndexesXOR ^= _count; {
page->Count++; page.Indexes[entityID & PageSlot.MASK] = _count;
page.IndexesXOR ^= _count;
}
//if ((page.Count == 0 && page.IndexesXOR != 0) ||
// (page.Count < 0) ||
// (page.Count > 1 && page.Indexes == _nullPage) ||
// (page.Count <= 1 && page.Indexes != _nullPage))
//{
// throw new Exception();
//}
//Console.WriteLine($"+ {page.Count} {page.Indexes == _nullPage}");
} }
public void RemoveUnchecked(int entityID) public void RemoveUnchecked(int entityID)
@ -425,27 +452,45 @@ namespace DCFApixels.DragonECS
//_sparse[_dense[_count--]] = _sparse[entityID]; //_sparse[_dense[_count--]] = _sparse[entityID];
//_sparse[entityID] = 0; //_sparse[entityID] = 0;
PageSlot* page = _sparsePages + (entityID >> PageSlot.SHIFT); ref PageSlot page = ref _sparsePages[entityID >> PageSlot.SHIFT];
int localEntityID = entityID & PageSlot.MASK; int localEntityID = entityID & PageSlot.MASK;
_dense[page->Indexes[localEntityID]] = _dense[_count]; _dense[page.Indexes[localEntityID]] = _dense[_count];
int localLastIndex = _dense[_count--]; int localLastIndex = _dense[_count--];
int* lastPageArray = (_sparsePages + (localLastIndex >> PageSlot.SHIFT))->Indexes; int* lastPageArray = (_sparsePages + (localLastIndex >> PageSlot.SHIFT))->Indexes;
localLastIndex = localLastIndex & PageSlot.MASK; localLastIndex = localLastIndex & PageSlot.MASK;
lastPageArray[localLastIndex] = page->Indexes[localEntityID]; lastPageArray[localLastIndex] = page.Indexes[localEntityID];
if (--page->Count == 0) if (--page.Count == 0)
{ {
_source.ReturnPage(page->Indexes); page.IndexesXOR = 0;
//page->IndexesXOR = 0; page.Count = 0;
page->Indexes = _nullPage;
} }
else else
{ {
//page->IndexesXOR ^= page->Indexes[localEntityID]; page.IndexesXOR ^= page.Indexes[localEntityID];
page->Indexes[localEntityID] = 0; page.Indexes[localEntityID] = 0;
if (page.Count == 1)
{
int* x = _nullPage;
if (page.Indexes == x)
{
}
_source.ReturnPage(page.Indexes);
page.Indexes = _nullPage;
}
} }
//if((page.Count == 0 && page.IndexesXOR != 0) ||
// (page.Count < 0) ||
// (page.Count > 1 && page.Indexes == _nullPage) ||
// (page.Count <= 1 && page.Indexes != _nullPage))
//{
// throw new Exception();
//}
//Console.WriteLine($"- {page.Count} {page.Indexes == _nullPage}");
} }
public void RemoveUnusedEntityIDs() public void RemoveUnusedEntityIDs()
@ -473,19 +518,19 @@ namespace DCFApixels.DragonECS
//} //}
for (int i = 0; i < _sparsePagesCount; i++) for (int i = 0; i < _sparsePagesCount; i++)
{ {
var page = _sparsePages + i; ref PageSlot page = ref _sparsePages[i];
if (page->Indexes != _nullPage) if (page.Indexes != _nullPage)
{ {
//TODO тут надо оптимизировать отчисткой не всего а по dense списку //TODO тут надо оптимизировать отчисткой не всего а по dense списку
for (int j = 0; j < PageSlot.SIZE; j++) for (int j = 0; j < PageSlot.SIZE; j++)
{ {
page->Indexes[j] = 0; page.Indexes[j] = 0;
} }
_source.ReturnPage(page->Indexes); _source.ReturnPage(page.Indexes);
page->Indexes = _nullPage; page.Indexes = _nullPage;
} }
//page->IndexesXOR = 0; page.IndexesXOR = 0;
page->Count = 0; page.Count = 0;
} }
_count = 0; _count = 0;
@ -1495,22 +1540,48 @@ namespace DCFApixels.DragonECS
{ {
return (capacity >> PageSlot.SHIFT) + ((capacity & PageSlot.MASK) == 0 ? 0 : 1); return (capacity >> PageSlot.SHIFT) + ((capacity & PageSlot.MASK) == 0 ? 0 : 1);
} }
[DebuggerTypeProxy(typeof(DebuggerProxy))]
private struct PageSlot private struct PageSlot
{ {
public const int SHIFT = 6; // 64 public const int SHIFT = 6; // 64
public const int SIZE = 1 << SHIFT; public const int SIZE = 1 << SHIFT;
public const int MASK = SIZE - 1; public const int MASK = SIZE - 1;
public static readonly PageSlot Empty = new PageSlot(_nullPage, 0, 0); public static readonly PageSlot Empty = new PageSlot(_nullPage);
public int* Indexes; public int* Indexes;
//public int IndexesXOR; public int IndexesXOR;
public byte Count; public sbyte Count;
public PageSlot(int* indexes, int indexesXOR, byte count) public PageSlot(int* indexes)
{ {
Indexes = indexes; Indexes = indexes;
//IndexesXOR = indexesXOR; IndexesXOR = 0;
Count = count; Count = 0;
}
private class DebuggerProxy
{
private PageSlot _page;
public int[] Indexes;
public IntPtr IndexesPtr;
public bool IsNullPage
{
get { return IndexesPtr == (IntPtr)_nullPagePtrFake; }
}
public int IndexesXOR;
public sbyte Count;
public DebuggerProxy(PageSlot page)
{
_page = page;
Indexes = new int[SIZE];
for (int i = 0; i < SIZE; i++)
{
Indexes[i] = page.Indexes[i];
}
IndexesPtr = (IntPtr)page.Indexes;
IndexesXOR = page.IndexesXOR;
Count = page.Count;
}
} }
} }
#endregion #endregion