Add SparseSet Test

This commit is contained in:
2025-12-13 18:26:59 +09:00
parent c882c75760
commit a5df2c9637
12 changed files with 179 additions and 102 deletions

View File

@@ -26,6 +26,6 @@ using Misaki.HighPerformance.Collections;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
var csm = new UnsafeSlotMap<int>();
Console.WriteLine(csm.Contains(0, 0));
Console.WriteLine(csm.Count == 0);
AllocationManager.EnableDebugLayer();
using var csm = new UnsafeSlotMap<int>(4, Allocator.Persistent);
AllocationManager.Dispose();

View File

@@ -4,14 +4,20 @@ using System.Collections.Concurrent;
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
[TestClass]
public class TestSlotMap
public class TestConcurrentSlotMap
{
private SlotMap<int> _slotMap = null!;
private ConcurrentSlotMap<int> _slotMap = null!;
public TestContext TestContext
{
get;
set;
}
[TestInitialize]
public void Initialize()
{
_slotMap = new SlotMap<int>();
_slotMap = new ConcurrentSlotMap<int>();
}
[TestMethod]
@@ -57,4 +63,65 @@ public class TestSlotMap
Assert.AreEqual(slotIndex1, slotIndex2);
Assert.AreNotEqual(generation1, generation2);
}
[TestMethod]
public void TestConcurrentAdditions()
{
const int threadCount = 8;
const int itemsPerThread = 1000;
var tasks = new List<Task>();
for (int t = 0; t < threadCount; t++)
{
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < itemsPerThread; i++)
{
_slotMap.Add(i, out _);
}
}, TestContext.CancellationTokenSource.Token));
}
Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token);
Assert.AreEqual(threadCount * itemsPerThread, _slotMap.Count);
}
[TestMethod]
public void TestConcurrentRandomAddRemove()
{
const int threadCount = 8;
const int operationsPerThread = 1000;
var tasks = new List<Task>();
var rand = new Random();
var addedItems = new ConcurrentBag<(int slotIndex, int generation)>();
var count = 0;
for (int t = 0; t < threadCount; t++)
{
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < operationsPerThread; i++)
{
if (rand.NextDouble() < 0.5)
{
var slotIndex = _slotMap.Add(i, out var generation);
addedItems.Add((slotIndex, generation));
Interlocked.Increment(ref count);
}
else if (addedItems.TryTake(out var item))
{
_slotMap.Remove(item.slotIndex, item.generation);
Interlocked.Decrement(ref count);
}
}
}, TestContext.CancellationTokenSource.Token));
}
Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token);
Assert.AreEqual(count, _slotMap.Count);
}
}

View File

@@ -4,20 +4,14 @@ using System.Collections.Concurrent;
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
[TestClass]
public class TestConcurrentSlotMap
public class TestSlotMap
{
private ConcurrentSlotMap<int> _slotMap = null!;
public TestContext TestContext
{
get;
set;
}
private SlotMap<int> _slotMap = null!;
[TestInitialize]
public void Initialize()
{
_slotMap = new ConcurrentSlotMap<int>();
_slotMap = new SlotMap<int>();
}
[TestMethod]
@@ -63,65 +57,4 @@ public class TestConcurrentSlotMap
Assert.AreEqual(slotIndex1, slotIndex2);
Assert.AreNotEqual(generation1, generation2);
}
[TestMethod]
public void TestConcurrentAdditions()
{
const int threadCount = 8;
const int itemsPerThread = 1000;
var tasks = new List<Task>();
for (int t = 0; t < threadCount; t++)
{
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < itemsPerThread; i++)
{
_slotMap.Add(i, out _);
}
}, TestContext.CancellationTokenSource.Token));
}
Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token);
Assert.AreEqual(threadCount * itemsPerThread, _slotMap.Count);
}
[TestMethod]
public void TestConcurrentRandomAddRemove()
{
const int threadCount = 8;
const int operationsPerThread = 1000;
var tasks = new List<Task>();
var rand = new Random();
var addedItems = new ConcurrentBag<(int slotIndex, int generation)>();
var count = 0;
for (int t = 0; t < threadCount; t++)
{
tasks.Add(Task.Run(() =>
{
for (int i = 0; i < operationsPerThread; i++)
{
if (rand.NextDouble() < 0.5)
{
var slotIndex = _slotMap.Add(i, out var generation);
addedItems.Add((slotIndex, generation));
Interlocked.Increment(ref count);
}
else if (addedItems.TryTake(out var item))
{
_slotMap.Remove(item.slotIndex, item.generation);
Interlocked.Decrement(ref count);
}
}
}, TestContext.CancellationTokenSource.Token));
}
Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token);
Assert.AreEqual(count, _slotMap.Count);
}
}

View File

@@ -0,0 +1,52 @@
using Misaki.HighPerformance.Collections;
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
[TestClass]
public class TestSparseSet
{
private SparseSet<int> _sparseSet = null!;
[TestInitialize]
public void Initialize()
{
_sparseSet = new SparseSet<int>(16);
}
[TestMethod]
public void InvalidID()
{
Assert.IsFalse(_sparseSet.Contains(0, 0));
}
[TestMethod]
public void Add()
{
var id = _sparseSet.Add(10, out var gen);
Assert.IsTrue(_sparseSet.Contains(id, gen));
}
[TestMethod]
public void Remove()
{
var id = _sparseSet.Add(20, out var gen);
Assert.IsTrue(_sparseSet.Contains(id, gen));
_sparseSet.Remove(id, gen);
Assert.IsFalse(_sparseSet.Contains(id, gen));
}
[TestMethod]
public void IndexReuse()
{
var id = _sparseSet.Add(20, out var gen);
Assert.IsTrue(_sparseSet.Contains(id, gen));
_sparseSet.Remove(id, gen);
Assert.IsFalse(_sparseSet.Contains(id, gen));
var newId = _sparseSet.Add(30, out var newGen);
Assert.AreEqual(id, newId);
Assert.AreNotEqual(gen, newGen);
}
}

View File

@@ -20,6 +20,12 @@ public class TestUnsafeSparseSet
_sparseSet.Dispose();
}
[TestMethod]
public void InvalidID()
{
Assert.IsFalse(_sparseSet.Contains(0, 0));
}
[TestMethod]
public void Add()
{
@@ -139,4 +145,4 @@ public class TestUnsafeSparseSet
Assert.AreEqual(2, index);
}
}
}