优化对象池查找速度

This commit is contained in:
陈思海 2026-03-23 20:50:11 +08:00
parent 9da67afe50
commit d5518c46b1
4 changed files with 205 additions and 44 deletions

View File

@ -221,6 +221,16 @@ namespace AlicizaX
return m_LinkedList.Contains(value); return m_LinkedList.Contains(value);
} }
/// <summary>
/// 确定指定结点是否在链表中。
/// </summary>
/// <param name="node">要检查的结点。</param>
/// <returns>指定结点是否在链表中。</returns>
public bool Contains(LinkedListNode<T> node)
{
return node != null && node.List == m_LinkedList;
}
/// <summary> /// <summary>
/// 从目标数组的指定索引处开始将整个链表复制到兼容的一维数组。 /// 从目标数组的指定索引处开始将整个链表复制到兼容的一维数组。
/// </summary> /// </summary>

View File

@ -101,18 +101,19 @@ namespace AlicizaX
/// </summary> /// </summary>
/// <param name="key">指定的主键。</param> /// <param name="key">指定的主键。</param>
/// <param name="value">指定的值。</param> /// <param name="value">指定的值。</param>
public void Add(TKey key, TValue value) public LinkedListNode<TValue> Add(TKey key, TValue value)
{ {
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>); GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (m_Dictionary.TryGetValue(key, out range)) if (m_Dictionary.TryGetValue(key, out range))
{ {
m_LinkedList.AddBefore(range.Terminal, value); return m_LinkedList.AddBefore(range.Terminal, value);
} }
else else
{ {
LinkedListNode<TValue> first = m_LinkedList.AddLast(value); LinkedListNode<TValue> first = m_LinkedList.AddLast(value);
LinkedListNode<TValue> terminal = m_LinkedList.AddLast(default(TValue)); LinkedListNode<TValue> terminal = m_LinkedList.AddLast(default(TValue));
m_Dictionary.Add(key, new GameFrameworkLinkedListRange<TValue>(first, terminal)); m_Dictionary.Add(key, new GameFrameworkLinkedListRange<TValue>(first, terminal));
return first;
} }
} }
@ -131,9 +132,41 @@ namespace AlicizaX
{ {
if (current.Value.Equals(value)) if (current.Value.Equals(value))
{ {
if (current == range.First) return Remove(key, current);
}
}
}
return false;
}
/// <summary>
/// 从指定的主键中移除指定结点。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <param name="node">要移除的结点。</param>
/// <returns>是否移除成功。</returns>
public bool Remove(TKey key, LinkedListNode<TValue> node)
{ {
LinkedListNode<TValue> next = current.Next; if (node == null)
{
return false;
}
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (!m_Dictionary.TryGetValue(key, out range))
{
return false;
}
if (!m_LinkedList.Contains(node) || node == range.Terminal)
{
return false;
}
if (node == range.First)
{
LinkedListNode<TValue> next = node.Next;
if (next == range.Terminal) if (next == range.Terminal)
{ {
m_LinkedList.Remove(next); m_LinkedList.Remove(next);
@ -145,14 +178,9 @@ namespace AlicizaX
} }
} }
m_LinkedList.Remove(current); m_LinkedList.Remove(node);
return true; return true;
} }
}
}
return false;
}
/// <summary> /// <summary>
/// 从指定的主键中移除所有的值。 /// 从指定的主键中移除所有的值。

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using AlicizaX; using AlicizaX;
namespace AlicizaX.ObjectPool namespace AlicizaX.ObjectPool
@ -11,16 +12,20 @@ namespace AlicizaX.ObjectPool
/// <typeparam name="T">对象类型。</typeparam> /// <typeparam name="T">对象类型。</typeparam>
private sealed class Object<T> : IMemory where T : ObjectBase private sealed class Object<T> : IMemory where T : ObjectBase
{ {
private string m_Name;
private T m_Object; private T m_Object;
private int m_SpawnCount; private int m_SpawnCount;
private LinkedListNode<Object<T>> m_NameNode;
/// <summary> /// <summary>
/// 初始化内部对象的新实例。 /// 初始化内部对象的新实例。
/// </summary> /// </summary>
public Object() public Object()
{ {
m_Name = null;
m_Object = null; m_Object = null;
m_SpawnCount = 0; m_SpawnCount = 0;
m_NameNode = null;
} }
/// <summary> /// <summary>
@ -28,7 +33,7 @@ namespace AlicizaX.ObjectPool
/// </summary> /// </summary>
public string Name public string Name
{ {
get { return m_Object.Name; } get { return m_Name; }
} }
/// <summary> /// <summary>
@ -81,6 +86,15 @@ namespace AlicizaX.ObjectPool
get { return m_SpawnCount; } get { return m_SpawnCount; }
} }
/// <summary>
/// 获取或设置对象在名字桶中的链表结点。
/// </summary>
public LinkedListNode<Object<T>> NameNode
{
get { return m_NameNode; }
internal set { m_NameNode = value; }
}
/// <summary> /// <summary>
/// 创建内部对象。 /// 创建内部对象。
/// </summary> /// </summary>
@ -95,6 +109,7 @@ namespace AlicizaX.ObjectPool
} }
Object<T> internalObject = MemoryPool.Acquire<Object<T>>(); Object<T> internalObject = MemoryPool.Acquire<Object<T>>();
internalObject.m_Name = obj.Name;
internalObject.m_Object = obj; internalObject.m_Object = obj;
internalObject.m_SpawnCount = spawned ? 1 : 0; internalObject.m_SpawnCount = spawned ? 1 : 0;
if (spawned) if (spawned)
@ -110,8 +125,10 @@ namespace AlicizaX.ObjectPool
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
m_Name = null;
m_Object = null; m_Object = null;
m_SpawnCount = 0; m_SpawnCount = 0;
m_NameNode = null;
} }
/// <summary> /// <summary>

View File

@ -15,8 +15,10 @@ namespace AlicizaX.ObjectPool
private readonly GameFrameworkMultiDictionary<string, Object<T>> m_Objects; private readonly GameFrameworkMultiDictionary<string, Object<T>> m_Objects;
private readonly Dictionary<object, Object<T>> m_ObjectMap; private readonly Dictionary<object, Object<T>> m_ObjectMap;
private readonly ReleaseObjectFilterCallback<T> m_DefaultReleaseObjectFilterCallback; private readonly ReleaseObjectFilterCallback<T> m_DefaultReleaseObjectFilterCallback;
private readonly List<T> m_CachedCanReleaseObjects; private readonly List<Object<T>> m_CachedCanReleaseObjects;
private readonly List<T> m_CachedToReleaseObjects; private readonly List<Object<T>> m_CachedToReleaseObjects;
private readonly List<T> m_CachedCanReleaseTargets;
private readonly IComparer<Object<T>> m_ReleaseObjectComparer;
private readonly bool m_AllowMultiSpawn; private readonly bool m_AllowMultiSpawn;
private float m_AutoReleaseInterval; private float m_AutoReleaseInterval;
private int m_Capacity; private int m_Capacity;
@ -39,8 +41,10 @@ namespace AlicizaX.ObjectPool
m_Objects = new GameFrameworkMultiDictionary<string, Object<T>>(); m_Objects = new GameFrameworkMultiDictionary<string, Object<T>>();
m_ObjectMap = new Dictionary<object, Object<T>>(); m_ObjectMap = new Dictionary<object, Object<T>>();
m_DefaultReleaseObjectFilterCallback = DefaultReleaseObjectFilterCallback; m_DefaultReleaseObjectFilterCallback = DefaultReleaseObjectFilterCallback;
m_CachedCanReleaseObjects = new List<T>(); m_CachedCanReleaseObjects = new List<Object<T>>();
m_CachedToReleaseObjects = new List<T>(); m_CachedToReleaseObjects = new List<Object<T>>();
m_CachedCanReleaseTargets = new List<T>();
m_ReleaseObjectComparer = Comparer<Object<T>>.Create(ReleaseObjectComparer);
m_AllowMultiSpawn = allowMultiSpawn; m_AllowMultiSpawn = allowMultiSpawn;
m_AutoReleaseInterval = autoReleaseInterval; m_AutoReleaseInterval = autoReleaseInterval;
Capacity = capacity; Capacity = capacity;
@ -163,7 +167,7 @@ namespace AlicizaX.ObjectPool
} }
Object<T> internalObject = Object<T>.Create(obj, spawned); Object<T> internalObject = Object<T>.Create(obj, spawned);
m_Objects.Add(obj.Name, internalObject); internalObject.NameNode = m_Objects.Add(internalObject.Name, internalObject);
m_ObjectMap.Add(obj.Target, internalObject); m_ObjectMap.Add(obj.Target, internalObject);
if (Count > m_Capacity) if (Count > m_Capacity)
@ -393,17 +397,7 @@ namespace AlicizaX.ObjectPool
return false; return false;
} }
if (internalObject.IsInUse || internalObject.Locked || !internalObject.CustomCanReleaseFlag) return ReleaseObject(internalObject);
{
return false;
}
m_Objects.Remove(internalObject.Name, internalObject);
m_ObjectMap.Remove(internalObject.Peek().Target);
internalObject.Release(false);
MemoryPool.Release(internalObject);
return true;
} }
/// <summary> /// <summary>
@ -444,6 +438,12 @@ namespace AlicizaX.ObjectPool
throw new GameFrameworkException("Release object filter callback is invalid."); throw new GameFrameworkException("Release object filter callback is invalid.");
} }
if (releaseObjectFilterCallback == m_DefaultReleaseObjectFilterCallback)
{
ReleaseWithDefaultFilter(toReleaseCount);
return;
}
if (toReleaseCount < 0) if (toReleaseCount < 0)
{ {
toReleaseCount = 0; toReleaseCount = 0;
@ -457,7 +457,13 @@ namespace AlicizaX.ObjectPool
m_AutoReleaseTime = 0f; m_AutoReleaseTime = 0f;
GetCanReleaseObjects(m_CachedCanReleaseObjects); GetCanReleaseObjects(m_CachedCanReleaseObjects);
List<T> toReleaseObjects = releaseObjectFilterCallback(m_CachedCanReleaseObjects, toReleaseCount, expireTime); m_CachedCanReleaseTargets.Clear();
foreach (Object<T> internalObject in m_CachedCanReleaseObjects)
{
m_CachedCanReleaseTargets.Add(internalObject.Peek());
}
List<T> toReleaseObjects = releaseObjectFilterCallback(m_CachedCanReleaseTargets, toReleaseCount, expireTime);
if (toReleaseObjects == null || toReleaseObjects.Count <= 0) if (toReleaseObjects == null || toReleaseObjects.Count <= 0)
{ {
return; return;
@ -476,7 +482,7 @@ namespace AlicizaX.ObjectPool
{ {
m_AutoReleaseTime = 0f; m_AutoReleaseTime = 0f;
GetCanReleaseObjects(m_CachedCanReleaseObjects); GetCanReleaseObjects(m_CachedCanReleaseObjects);
foreach (T toReleaseObject in m_CachedCanReleaseObjects) foreach (Object<T> toReleaseObject in m_CachedCanReleaseObjects)
{ {
ReleaseObject(toReleaseObject); ReleaseObject(toReleaseObject);
} }
@ -523,6 +529,7 @@ namespace AlicizaX.ObjectPool
m_ObjectMap.Clear(); m_ObjectMap.Clear();
m_CachedCanReleaseObjects.Clear(); m_CachedCanReleaseObjects.Clear();
m_CachedToReleaseObjects.Clear(); m_CachedToReleaseObjects.Clear();
m_CachedCanReleaseTargets.Clear();
} }
private Object<T> GetObject(object target) private Object<T> GetObject(object target)
@ -541,7 +548,95 @@ namespace AlicizaX.ObjectPool
return null; return null;
} }
private void GetCanReleaseObjects(List<T> results) private bool ReleaseObject(Object<T> internalObject)
{
if (internalObject == null)
{
return false;
}
if (internalObject.IsInUse || internalObject.Locked || !internalObject.CustomCanReleaseFlag)
{
return false;
}
T targetObject = internalObject.Peek();
if (targetObject == null)
{
return false;
}
if (!m_Objects.Remove(internalObject.Name, internalObject.NameNode))
{
m_Objects.Remove(internalObject.Name, internalObject);
}
internalObject.NameNode = null;
m_ObjectMap.Remove(targetObject.Target);
internalObject.Release(false);
MemoryPool.Release(internalObject);
return true;
}
private void ReleaseWithDefaultFilter(int toReleaseCount)
{
if (toReleaseCount < 0)
{
toReleaseCount = 0;
}
DateTime expireTime = DateTime.MinValue;
bool checkExpire = m_ExpireTime < float.MaxValue;
if (checkExpire)
{
expireTime = DateTime.UtcNow.AddSeconds(-m_ExpireTime);
}
m_AutoReleaseTime = 0f;
if (Count <= 0 || (toReleaseCount <= 0 && !checkExpire))
{
return;
}
GetCanReleaseObjects(m_CachedCanReleaseObjects);
if (m_CachedCanReleaseObjects.Count <= 0)
{
return;
}
m_CachedToReleaseObjects.Clear();
int remainingCount = 0;
for (int i = 0; i < m_CachedCanReleaseObjects.Count; i++)
{
Object<T> candidateObject = m_CachedCanReleaseObjects[i];
if (checkExpire && candidateObject.LastUseTime <= expireTime)
{
m_CachedToReleaseObjects.Add(candidateObject);
continue;
}
m_CachedCanReleaseObjects[remainingCount++] = candidateObject;
}
int additionalReleaseCount = toReleaseCount - m_CachedToReleaseObjects.Count;
if (additionalReleaseCount > 0 && remainingCount > 0)
{
m_CachedCanReleaseObjects.Sort(0, remainingCount, m_ReleaseObjectComparer);
int takeCount = additionalReleaseCount < remainingCount ? additionalReleaseCount : remainingCount;
for (int i = 0; i < takeCount; i++)
{
m_CachedToReleaseObjects.Add(m_CachedCanReleaseObjects[i]);
}
}
foreach (Object<T> toReleaseObject in m_CachedToReleaseObjects)
{
ReleaseObject(toReleaseObject);
}
}
private void GetCanReleaseObjects(List<Object<T>> results)
{ {
if (results == null) if (results == null)
{ {
@ -557,13 +652,13 @@ namespace AlicizaX.ObjectPool
continue; continue;
} }
results.Add(internalObject.Peek()); results.Add(internalObject);
} }
} }
private List<T> DefaultReleaseObjectFilterCallback(List<T> candidateObjects, int toReleaseCount, DateTime expireTime) private List<T> DefaultReleaseObjectFilterCallback(List<T> candidateObjects, int toReleaseCount, DateTime expireTime)
{ {
m_CachedToReleaseObjects.Clear(); m_CachedCanReleaseTargets.Clear();
if (expireTime > DateTime.MinValue) if (expireTime > DateTime.MinValue)
{ {
@ -571,13 +666,13 @@ namespace AlicizaX.ObjectPool
{ {
if (candidateObjects[i].LastUseTime <= expireTime) if (candidateObjects[i].LastUseTime <= expireTime)
{ {
m_CachedToReleaseObjects.Add(candidateObjects[i]); m_CachedCanReleaseTargets.Add(candidateObjects[i]);
candidateObjects.RemoveAt(i); candidateObjects.RemoveAt(i);
continue; continue;
} }
} }
toReleaseCount -= m_CachedToReleaseObjects.Count; toReleaseCount -= m_CachedCanReleaseTargets.Count;
} }
for (int i = 0; toReleaseCount > 0 && i < candidateObjects.Count; i++) for (int i = 0; toReleaseCount > 0 && i < candidateObjects.Count; i++)
@ -593,11 +688,22 @@ namespace AlicizaX.ObjectPool
} }
} }
m_CachedToReleaseObjects.Add(candidateObjects[i]); m_CachedCanReleaseTargets.Add(candidateObjects[i]);
toReleaseCount--; toReleaseCount--;
} }
return m_CachedToReleaseObjects; return m_CachedCanReleaseTargets;
}
private static int ReleaseObjectComparer(Object<T> a, Object<T> b)
{
int priorityCompare = a.Priority.CompareTo(b.Priority);
if (priorityCompare != 0)
{
return priorityCompare;
}
return a.LastUseTime.CompareTo(b.LastUseTime);
} }
} }
} }