From f9af1086e50081de0ab1207bb57458776db08126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Lema=C5=84czyk?= Date: Mon, 14 Jul 2025 06:24:40 +0200 Subject: [PATCH] Use pooled arrays in RecyclableDictionary --- .../RecyclableDictionary.cs | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/Recyclable.Collections/RecyclableDictionary.cs b/Recyclable.Collections/RecyclableDictionary.cs index 736ae74..c8da1e3 100644 --- a/Recyclable.Collections/RecyclableDictionary.cs +++ b/Recyclable.Collections/RecyclableDictionary.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; +using Recyclable.Collections.Pools; namespace Recyclable.Collections { @@ -14,7 +15,9 @@ namespace Recyclable.Collections private readonly int _blockSizeMinus1; private readonly byte _blockShift; +#nullable disable private Entry[][] _entries; +#nullable restore private int[] _buckets; private int _count; private bool _disposed; @@ -38,8 +41,12 @@ namespace Recyclable.Collections _blockSizeMinus1 = blockSize - 1; _blockShift = (byte)(31 - BitOperations.LeadingZeroCount((uint)blockSize)); - _entries = new Entry[4][]; - _entries[0] = new Entry[blockSize]; + _entries = 4 >= RecyclableDefaults.MinPooledArrayLength + ? RecyclableArrayPool.RentShared(4) + : new Entry[4][]; + _entries[0] = blockSize >= RecyclableDefaults.MinPooledArrayLength + ? RecyclableArrayPool.RentShared(blockSize) + : new Entry[blockSize]; _buckets = new int[4]; Array.Fill(_buckets, -1); } @@ -171,11 +178,37 @@ namespace Recyclable.Collections public void Clear() { - if (_needsClearing) + if (_count == 0) { - for (var i = 0; i < _count; i++) + Array.Fill(_buckets, -1); + return; + } + + int blockIndex = 0; + if (_blockSize >= RecyclableDefaults.MinPooledArrayLength) + { + while (blockIndex < _entries.Length && _entries[blockIndex] != null) { - ClearEntry(i); + RecyclableArrayPool.ReturnShared(_entries[blockIndex]!, _needsClearing); + _entries[blockIndex++] = null!; + } + } + else + { + if (_needsClearing) + { + while (blockIndex < _entries.Length && _entries[blockIndex] != null) + { + Array.Clear(_entries[blockIndex]!, 0, _entries[blockIndex]!.Length); + _entries[blockIndex++] = null!; + } + } + else + { + while (blockIndex < _entries.Length && _entries[blockIndex] != null) + { + _entries[blockIndex++] = null!; + } } } @@ -201,6 +234,16 @@ namespace Recyclable.Collections } Clear(); + if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(_entries, false); + } + + if (_buckets.Length >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(_buckets, false); + } + _entries = Array.Empty(); _buckets = Array.Empty(); _disposed = true; @@ -254,12 +297,28 @@ namespace Recyclable.Collections int requiredBlocks = (min + _blockSizeMinus1) >> _blockShift; if (_entries.Length < requiredBlocks) { - Array.Resize(ref _entries, requiredBlocks); + var newBlocks = requiredBlocks >= RecyclableDefaults.MinPooledArrayLength + ? RecyclableArrayPool.RentShared(requiredBlocks) + : new Entry[requiredBlocks][]; + + Array.Copy(_entries, newBlocks, _entries.Length); + + if (_entries.Length >= RecyclableDefaults.MinPooledArrayLength) + { + RecyclableArrayPool.ReturnShared(_entries, false); + } + + _entries = newBlocks; } for (int i = 0; i < requiredBlocks; i++) { - _entries[i] ??= new Entry[_blockSize]; + if (_entries[i] == null) + { + _entries[i] = _blockSize >= RecyclableDefaults.MinPooledArrayLength + ? RecyclableArrayPool.RentShared(_blockSize) + : new Entry[_blockSize]; + } } }