Skip to content

Commit

Permalink
Added bassic object pools
Browse files Browse the repository at this point in the history
  • Loading branch information
nbollis committed Jan 14, 2025
1 parent dc44773 commit 8620b8f
Show file tree
Hide file tree
Showing 7 changed files with 299 additions and 0 deletions.
1 change: 1 addition & 0 deletions mzLib/MzLibUtil/MzLibUtil.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<PackageReference Include="CsvHelper" Version="32.0.3" />
<PackageReference Include="Easy.Common" Version="6.7.0" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
</ItemGroup>

Expand Down
2 changes: 2 additions & 0 deletions mzLib/MzLibUtil/MzLibUtil.csproj.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=objectpools/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
58 changes: 58 additions & 0 deletions mzLib/MzLibUtil/ObjectPools/DictionaryPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.ObjectPool;

namespace MzLibUtil;

// Used to pool HashSet instances to reduce memory allocations
public class DictionaryPool<TKey, TValue> where TKey : notnull
{
private readonly ObjectPool<Dictionary<TKey, TValue>> _pool;

/// <summary>
/// Initializes a new instance of the <see cref="DictionaryPool{TKey, TValue}"/> class.
/// </summary>
/// <param name="initialCapacity">Initial capacity for the pooled Dictionary instances.</param>
public DictionaryPool(int initialCapacity = 16)
{
var policy = new DictionaryPooledObjectPolicy<TKey, TValue>(initialCapacity);
var provider = new DefaultObjectPoolProvider { MaximumRetained = Environment.ProcessorCount * 2 };
_pool = provider.Create(policy);
}

/// <summary>
/// Retrieves a Dictionary instance from the pool.
/// </summary>
/// <returns>A Dictionary instance.</returns>
public Dictionary<TKey, TValue> Get() => _pool.Get();

/// <summary>
/// Returns a Dictionary instance back to the pool.
/// </summary>
/// <param name="dictionary">The Dictionary instance to return.</param>
public void Return(Dictionary<TKey, TValue> dictionary)
{
if (dictionary == null) throw new ArgumentNullException(nameof(dictionary));
dictionary.Clear(); // Ensure the Dictionary is clean before returning it to the pool
_pool.Return(dictionary);
}

private class DictionaryPooledObjectPolicy<TKeyItem, TValueItem>(int initialCapacity)
: PooledObjectPolicy<Dictionary<TKeyItem, TValueItem>>
where TKeyItem : notnull
{
private int InitialCapacity { get; } = initialCapacity;

public override Dictionary<TKeyItem, TValueItem> Create()
{
return new Dictionary<TKeyItem, TValueItem>(capacity: InitialCapacity);
}

public override bool Return(Dictionary<TKeyItem, TValueItem> obj)
{
// Ensure the Dictionary can be safely reused
obj.Clear();
return true;
}
}
}
60 changes: 60 additions & 0 deletions mzLib/MzLibUtil/ObjectPools/HashSetPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.ObjectPool;

namespace MzLibUtil;

// Example Usage:
// var pool = new HashSetPool<int>();
// var hashSet = pool.Get();
// hashSet.Add(1);
// Do Work
// pool.Return(hashSet);

// Used to pool HashSet instances to reduce memory allocations
public class HashSetPool<T>
{
private readonly ObjectPool<HashSet<T>> _pool;

/// <summary>
/// Initializes a new instance of the <see cref="HashSetPool{T}"/> class.
/// </summary>
/// <param name="initialCapacity">Initial capacity for the pooled HashSet instances.</param>
public HashSetPool(int initialCapacity = 16)
{
var policy = new HashSetPooledObjectPolicy<T>(initialCapacity);
_pool = new DefaultObjectPool<HashSet<T>>(policy);
}

/// <summary>
/// Retrieves a HashSet instance from the pool.
/// </summary>
/// <returns>A HashSet instance.</returns>
public HashSet<T> Get() => _pool.Get();

/// <summary>
/// Returns a HashSet instance back to the pool.
/// </summary>
/// <param name="hashSet">The HashSet instance to return.</param>
public void Return(HashSet<T> hashSet)
{
if (hashSet == null) throw new ArgumentNullException(nameof(hashSet));
hashSet.Clear(); // Ensure the HashSet is clean before returning it to the pool
_pool.Return(hashSet);
}

private class HashSetPooledObjectPolicy<TItem>(int initialCapacity) : PooledObjectPolicy<HashSet<TItem>>
{
public override HashSet<TItem> Create()
{
return new HashSet<TItem>(capacity: initialCapacity);
}

public override bool Return(HashSet<TItem> obj)
{
// Ensure the HashSet can be safely reused
obj.Clear();
return true;
}
}
}
56 changes: 56 additions & 0 deletions mzLib/MzLibUtil/ObjectPools/ListPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.ObjectPool;

namespace MzLibUtil;

// Used to pool HashSet instances to reduce memory allocations
public class ListPool<T>
{
private readonly ObjectPool<List<T>> _pool;

/// <summary>
/// Initializes a new instance of the <see cref="ListPool{T}"/> class.
/// </summary>
/// <param name="initialCapacity">Initial capacity for the pooled HashSet instances.</param>
public ListPool(int initialCapacity = 16)
{
var policy = new ListPooledObjectPolicy<T>(initialCapacity);
var provider = new DefaultObjectPoolProvider { MaximumRetained = Environment.ProcessorCount * 2 };
_pool = provider.Create(policy);
}

/// <summary>
/// Retrieves a HashSet instance from the pool.
/// </summary>
/// <returns>A HashSet instance.</returns>
public List<T> Get() => _pool.Get();

/// <summary>
/// Returns a HashSet instance back to the pool.
/// </summary>
/// <param name="list">The HashSet instance to return.</param>
public void Return(List<T> list)
{
if (list == null) throw new ArgumentNullException(nameof(list));
list.Clear(); // Ensure the HashSet is clean before returning it to the pool
_pool.Return(list);
}

private class ListPooledObjectPolicy<TItem>(int initialCapacity) : PooledObjectPolicy<List<TItem>>
{
private int InitialCapacity { get; } = initialCapacity;

public override List<TItem> Create()
{
return new List<TItem>(capacity: InitialCapacity);
}

public override bool Return(List<TItem> obj)
{
// Ensure the HashSet can be safely reused
obj.Clear();
return true;
}
}
}
120 changes: 120 additions & 0 deletions mzLib/Test/ObjectPoolTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using MzLibUtil;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Test;

[TestFixture]
[ExcludeFromCodeCoverage]
public class HashSetPoolTests
{
[Test]
public void Get_ReturnsHashSetInstance()
{
var pool = new HashSetPool<int>();
var hashSet = pool.Get();
Assert.That(hashSet, Is.Not.Null);
pool.Return(hashSet);
}

[Test]
public void Return_ClearsHashSetBeforeReturningToPool()
{
var pool = new HashSetPool<int>();
var hashSet = pool.Get();
hashSet.Add(1);
pool.Return(hashSet);
Assert.That(hashSet.Count, Is.EqualTo(0));
}

[Test]
public void Return_ThrowsArgumentNullException_WhenHashSetIsNull()
{
var pool = new HashSetPool<int>();
Assert.Throws<ArgumentNullException>(() => pool.Return(null));
}
}

[TestFixture]
[ExcludeFromCodeCoverage]
public class DictionaryPoolTests
{
[Test]
public void Get_ReturnsDictionaryInstance()
{
var dictionaryPool = new DictionaryPool<string, int>();
var dictionary = dictionaryPool.Get();
Assert.That(dictionary, Is.Not.Null);
Assert.That(dictionary, Is.InstanceOf<Dictionary<string, int>>());
}

[Test]
public void Return_ClearsAndReturnsDictionaryToPool()
{
var dictionaryPool = new DictionaryPool<string, int>();
var dictionary = dictionaryPool.Get();
dictionary["key"] = 42;

dictionaryPool.Return(dictionary);

Assert.That(dictionary.Count, Is.EqualTo(0));
}

[Test]
public void Return_ThrowsArgumentNullException_WhenDictionaryIsNull()
{
var dictionaryPool = new DictionaryPool<string, int>();
Assert.Throws<ArgumentNullException>(() => dictionaryPool.Return(null));
}
}

[TestFixture]
[ExcludeFromCodeCoverage]
public class ListPoolTests
{
[Test]
public void ListPool_Get_ReturnsListWithInitialCapacity()
{
// Arrange
int initialCapacity = 16;
var listPool = new ListPool<int>(initialCapacity);

// Act
var list = listPool.Get();

// Assert
Assert.That(list, Is.Not.Null);
Assert.That(list.Capacity, Is.EqualTo(initialCapacity));
}

[Test]
public void ListPool_Return_ClearsListBeforeReturningToPool()
{
// Arrange
var listPool = new ListPool<int>();
var list = listPool.Get();
list.Add(1);
list.Add(2);

// Act
listPool.Return(list);
var returnedList = listPool.Get();

// Assert
Assert.That(returnedList, Is.Not.Null);
Assert.That(returnedList, Is.Empty);
}

[Test]
public void ListPool_Return_ThrowsArgumentNullException_WhenListIsNull()
{
// Arrange
var listPool = new ListPool<int>();

// Act & Assert
Assert.That(() => listPool.Return(null), Throws.ArgumentNullException);
}
}

2 changes: 2 additions & 0 deletions mzLib/mzLib.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<dependency id="OpenMcdf" version="2.2.1.3" />
<dependency id="OpenMcdf.Extensions" version="2.2.1.3" />
<dependency id="SharpLearning.Optimization" version="0.28.0" />
<dependency id="Microsoft.Extensions.ObjectPool" version="9.0.0" />
</group>
<group targetFramework="net8.0-windows7.0">
<!--net8.0 target with OxyPlot-->
Expand All @@ -36,6 +37,7 @@
<dependency id="OpenMcdf.Extensions" version="2.2.1.3" />
<dependency id="SharpLearning.Optimization" version="0.28.0" />
<dependency id="OxyPlot.Wpf" version="2.0.0" />
<dependency id="Microsoft.Extensions.ObjectPool" version="9.0.0" />
</group>
</dependencies>
</metadata>
Expand Down

0 comments on commit 8620b8f

Please sign in to comment.