Skip to content

Commit

Permalink
List pool usage extended to other valiable places
Browse files Browse the repository at this point in the history
  • Loading branch information
Raegan03 committed Jun 19, 2024
1 parent 60c3108 commit 68389f3
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 55 deletions.
66 changes: 66 additions & 0 deletions VContainer/Assets/Tests/ListPoolTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using NUnit.Framework;
using System.Collections.Generic;
using VContainer.Internal;

namespace VContainer.Tests
{
[TestFixture]
public class ListPoolTests
{
[Test]
public void Get_ShouldReturnNewList_WhenPoolIsEmpty()
{
var buffer = ListPool<int>.Get();

Assert.IsNotNull(buffer);
Assert.AreEqual(0, buffer.Count);
}

[Test]
public void Get_ShouldReturnListFromPool_WhenPoolIsNotEmpty()
{
var initialBuffer = ListPool<int>.Get();
ListPool<int>.Release(initialBuffer);

var buffer = ListPool<int>.Get();
Assert.AreSame(initialBuffer, buffer);
}

[Test]
public void Release_ShouldClearAndReturnListToPool()
{
var buffer = ListPool<int>.Get();
buffer.Add(1);

ListPool<int>.Release(buffer);
Assert.AreEqual(0, buffer.Count);
}

[Test]
public void BufferScope_ShouldReleaseBuffer_WhenDisposed()
{
List<int> buffer;
using (ListPool<int>.Get(out buffer))
{
buffer.Add(1);
}

Assert.AreEqual(0, buffer.Count);
}

[Test]
public void Get_And_Release_MultipleBuffers()
{
var buffer1 = ListPool<int>.Get();
var buffer2 = ListPool<int>.Get();

ListPool<int>.Release(buffer1);
ListPool<int>.Release(buffer2);

var bufferFromPool1 = ListPool<int>.Get();
var bufferFromPool2 = ListPool<int>.Get();

Assert.AreNotSame(bufferFromPool1, bufferFromPool2);
}
}
}
11 changes: 11 additions & 0 deletions VContainer/Assets/Tests/ListPoolTest.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -57,31 +57,36 @@ public object SpawnInstance(IObjectResolver resolver)
{
if (resolver is IScopedObjectResolver scope)
{
var entirelyRegistrations = CollectFromParentScopes(scope);
return SpawnInstance(resolver, entirelyRegistrations);
using (ListPool<Registration>.Get(out var entirelyRegistrations))
{
CollectFromParentScopes(scope, entirelyRegistrations);
return SpawnInstance(resolver, entirelyRegistrations);;
}
}
return SpawnInstance(resolver, registrations);
}

internal object SpawnInstance(
IObjectResolver resolver,
IReadOnlyList<Registration> registrations)
IReadOnlyList<Registration> entirelyRegistrations)
{
var array = Array.CreateInstance(ElementType, registrations.Count);
for (var i = 0; i < registrations.Count; i++)
var array = Array.CreateInstance(ElementType, entirelyRegistrations.Count);
for (var i = 0; i < entirelyRegistrations.Count; i++)
{
array.SetValue(resolver.Resolve(registrations[i]), i);
array.SetValue(resolver.Resolve(entirelyRegistrations[i]), i);
}
return array;
}

internal List<Registration> CollectFromParentScopes(
internal void CollectFromParentScopes(
IScopedObjectResolver scope,
List<Registration> registrationsBuffer,
bool localScopeOnly = false)
{
if (scope.Parent == null)
{
return registrations;
registrationsBuffer.AddRange(registrations);
return;
}

var finderType = InterfaceTypes[0];
Expand All @@ -93,11 +98,8 @@ internal List<Registration> CollectFromParentScopes(
if (scope.TryGetRegistration(finderType, out var registration) &&
registration.Provider is CollectionInstanceProvider parentCollection)
{
if (mergedRegistrations == null)
{
// TODO: object pooling
mergedRegistrations = new List<Registration>(registrations);
}
mergedRegistrations ??= ListPool<Registration>.Get();
mergedRegistrations.AddRange(registrations);

if (localScopeOnly)
{
Expand All @@ -116,7 +118,16 @@ internal List<Registration> CollectFromParentScopes(
}
scope = scope.Parent;
}
return mergedRegistrations ?? registrations;

if (mergedRegistrations == null)
{
registrationsBuffer.AddRange(registrations);
}
else
{
registrationsBuffer.AddRange(mergedRegistrations);
ListPool<Registration>.Release(mergedRegistrations);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ public object SpawnInstance(IObjectResolver resolver)
if (resolver is ScopedContainer scope &&
valueRegistration.Provider is CollectionInstanceProvider collectionProvider)
{
var entirelyRegistrations = collectionProvider.CollectFromParentScopes(scope, localScopeOnly: true);
value = collectionProvider.SpawnInstance(resolver, entirelyRegistrations);
using (ListPool<Registration>.Get(out var entirelyRegistrations))
{
collectionProvider.CollectFromParentScopes(scope, entirelyRegistrations, localScopeOnly: true);
value = collectionProvider.SpawnInstance(resolver, entirelyRegistrations);
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using System;
using System.Collections.Generic;

namespace VContainer.Unity
namespace VContainer.Internal
{
static class UnityEngineObjectListBuffer<T> where T : UnityEngine.Object
internal static class ListPool<T>
{
const int DefaultCapacity = 32;

[ThreadStatic]
private static Stack<List<T>> _pool = new Stack<List<T>>(4);
private static readonly Stack<List<T>> _pool = new Stack<List<T>>(4);

/// <summary>
/// BufferScope supports releasing a buffer with using clause.
/// </summary>
public struct BufferScope : IDisposable
internal readonly struct BufferScope : IDisposable
{
private readonly List<T> _buffer;

Expand All @@ -32,22 +31,25 @@ public void Dispose()
/// Get a buffer from the pool.
/// </summary>
/// <returns></returns>
public static List<T> Get()
internal static List<T> Get()
{
if (_pool.Count == 0)
lock (_pool)
{
return new List<T>(DefaultCapacity);
}
if (_pool.Count == 0)
{
return new List<T>(DefaultCapacity);
}

return _pool.Pop();
return _pool.Pop();
}
}

/// <summary>
/// Get a buffer from the pool. Returning a disposable struct to support recycling via using clause.
/// </summary>
/// <param name="buffer"></param>
/// <returns></returns>
public static BufferScope Get(out List<T> buffer)
internal static BufferScope Get(out List<T> buffer)
{
buffer = Get();
return new BufferScope(buffer);
Expand All @@ -57,10 +59,13 @@ public static BufferScope Get(out List<T> buffer)
/// Declare a buffer won't be used anymore and put it back to the pool.
/// </summary>
/// <param name="buffer"></param>
public static void Release(List<T> buffer)
internal static void Release(List<T> buffer)
{
buffer.Clear();
_pool.Push(buffer);
lock (_pool)
{
_pool.Push(buffer);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public object SpawnInstance(IObjectResolver resolver)
}
else if (scene.IsValid())
{
using (UnityEngineObjectListBuffer<GameObject>.Get(out var gameObjectBuffer))
using (ListPool<GameObject>.Get(out var gameObjectBuffer))
{
scene.GetRootGameObjects(gameObjectBuffer);
foreach (var gameObject in gameObjectBuffer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer.Internal;

namespace VContainer.Unity
{
Expand Down Expand Up @@ -45,22 +46,23 @@ static void AwakeWaitingChildren(LifetimeScope awakenParent)
{
if (WaitingList.Count <= 0) return;

var buf = new List<LifetimeScope>();

for (var i = WaitingList.Count - 1; i >= 0; i--)
using (ListPool<LifetimeScope>.Get(out var buffer))
{
var waitingScope = WaitingList[i];
if (waitingScope.parentReference.Type == awakenParent.GetType())
for (var i = WaitingList.Count - 1; i >= 0; i--)
{
waitingScope.parentReference.Object = awakenParent;
WaitingList.RemoveAt(i);
buf.Add(waitingScope);
var waitingScope = WaitingList[i];
if (waitingScope.parentReference.Type == awakenParent.GetType())
{
waitingScope.parentReference.Object = awakenParent;
WaitingList.RemoveAt(i);
buffer.Add(waitingScope);
}
}
}

foreach (var waitingScope in buf)
{
waitingScope.Awake();
foreach (var waitingScope in buffer)
{
waitingScope.Awake();
}
}
}

Expand All @@ -69,21 +71,22 @@ static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
if (WaitingList.Count <= 0)
return;

var buf = new List<LifetimeScope>();

for (var i = WaitingList.Count - 1; i >= 0; i--)
using (ListPool<LifetimeScope>.Get(out var buffer))
{
var waitingScope = WaitingList[i];
if (waitingScope.gameObject.scene == scene)
for (var i = WaitingList.Count - 1; i >= 0; i--)
{
WaitingList.RemoveAt(i);
buf.Add(waitingScope);
var waitingScope = WaitingList[i];
if (waitingScope.gameObject.scene == scene)
{
WaitingList.RemoveAt(i);
buffer.Add(waitingScope);
}
}
}

foreach (var waitingScope in buf)
{
waitingScope.Awake(); // Re-throw if parent not found
foreach (var waitingScope in buffer)
{
waitingScope.Awake(); // Re-throw if parent not found
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion VContainer/Assets/VContainer/Runtime/Unity/LifetimeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using VContainer.Diagnostics;
using VContainer.Internal;

namespace VContainer.Unity
{
Expand Down Expand Up @@ -101,7 +102,7 @@ public static ExtraInstallationScope Enqueue(IInstaller installer)

static LifetimeScope Find(Type type, Scene scene)
{
using (UnityEngineObjectListBuffer<GameObject>.Get(out var buffer))
using (ListPool<GameObject>.Get(out var buffer))
{
scene.GetRootGameObjects(buffer);
foreach (var gameObject in buffer)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using UnityEngine;
using VContainer.Internal;

namespace VContainer.Unity
{
Expand All @@ -10,7 +11,7 @@ void InjectGameObjectRecursive(GameObject current)
{
if (current == null) return;

using (UnityEngineObjectListBuffer<MonoBehaviour>.Get(out var buffer))
using (ListPool<MonoBehaviour>.Get(out var buffer))
{
buffer.Clear();
current.GetComponents(buffer);
Expand Down

0 comments on commit 68389f3

Please sign in to comment.