-
Notifications
You must be signed in to change notification settings - Fork 641
Reduce storage tracking allocations #9664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4945d02
Reduce storage tracking allocations
benaadams 7fbe3f4
formatting
benaadams 3534066
Formatting
benaadams 3cc9544
Switch to new
benaadams 8970791
Less changes
benaadams 1411f9a
Remove thread static
benaadams d4ac6aa
Use extension
benaadams ca9aaa0
Fast empty path
benaadams 4156658
Use faster checks for StackPool
benaadams e6b5915
Also reset skip flag
benaadams a23570d
Feedback
benaadams 7d995d1
Merge branch 'master' into allocations
benaadams e2f0b25
Fix
benaadams a1824ae
Merge branch 'allocations' of https://github.com/NethermindEth/nether…
benaadams 849555d
AI feedback
benaadams 6e91374
formatting
benaadams File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited | ||
| // SPDX-License-Identifier: LGPL-3.0-only | ||
|
|
||
| using System.Collections.Concurrent; | ||
| using System.Threading; | ||
| using Nethermind.Core.Resettables; | ||
|
|
||
| namespace Nethermind.Core.Caching; | ||
|
|
||
| /// <summary> | ||
| /// High performance static pool for reference types that support reset semantics. | ||
| /// </summary> | ||
| /// <typeparam name="T"> | ||
| /// The pooled type. Must be a reference type that implements <see cref="IResettable"/> and | ||
| /// has a public parameterless constructor. | ||
| /// </typeparam> | ||
| public static class StaticPool<T> where T : class, IResettable, new() | ||
| { | ||
| /// <summary> | ||
| /// Hard cap for the total number of items that can be stored in the shared pool. | ||
| /// Prevents unbounded growth under bursty workloads while still allowing reuse. | ||
| /// </summary> | ||
| private const int MaxPooledCount = 4096; | ||
|
|
||
| /// <summary> | ||
| /// Global pool shared between threads. | ||
| /// </summary> | ||
| private static readonly ConcurrentQueue<T> _pool = []; | ||
|
|
||
| /// <summary> | ||
| /// Manual count of items in the queue. | ||
| /// We maintain this separately because ConcurrentQueue.Count | ||
| /// is an O(n) traversal — it walks the internal segment chain. | ||
| /// Keeping our own count avoids that cost and keeps the hot path O(1). | ||
| /// </summary> | ||
| private static int _poolCount; | ||
|
|
||
| /// <summary> | ||
| /// Rents an instance of <typeparamref name="T"/> from the pool. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The method first attempts to dequeue an existing instance from the shared pool. | ||
| /// If the pool is empty, a new instance is created using the parameterless constructor. | ||
| /// </remarks> | ||
| /// <returns> | ||
| /// A reusable instance of <typeparamref name="T"/>. The returned instance is not guaranteed | ||
| /// to be zeroed or reset beyond the guarantees provided by <see cref="IResettable"/> and | ||
| /// the constructor. Callers should treat it as a freshly created instance. | ||
| /// </returns> | ||
| public static T Rent() | ||
| { | ||
| // Try to pop from the global pool — this is only hit when a thread | ||
| // has exhausted its own fast slot or is cross-thread renting. | ||
| if (Volatile.Read(ref _poolCount) > 0 && _pool.TryDequeue(out T? item)) | ||
| { | ||
| // We track count manually with Interlocked ops instead of using queue.Count. | ||
| Interlocked.Decrement(ref _poolCount); | ||
| return item; | ||
| } | ||
|
|
||
| // Nothing available, allocate new instance | ||
| return new(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns an instance of <typeparamref name="T"/> to the pool for reuse. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// The instance is reset via <see cref="IResettable.Reset"/> before being enqueued. | ||
| /// If adding the instance would exceed <see cref="MaxPooledCount"/>, the instance is | ||
| /// discarded and not pooled. | ||
| /// </remarks> | ||
| /// <param name="item"> | ||
| /// The instance to return to the pool. Must not be <see langword="null"/>. | ||
| /// After returning, the caller must not use the instance again. | ||
| /// </param> | ||
| public static void Return(T item) | ||
| { | ||
| // We use Interlocked.Increment to reserve a slot up front. | ||
| // This guarantees a bounded queue length without relying on slow Count(). | ||
| if (Interlocked.Increment(ref _poolCount) > MaxPooledCount) | ||
| { | ||
| // Roll back reservation if we'd exceed the cap. | ||
| Interlocked.Decrement(ref _poolCount); | ||
| return; | ||
| } | ||
|
|
||
| item.Reset(); | ||
| _pool.Enqueue(item); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
src/Nethermind/Nethermind.Core/Extensions/DictionaryExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited | ||
| // SPDX-License-Identifier: LGPL-3.0-only | ||
|
|
||
| using System.Collections.Generic; | ||
| using Nethermind.Core.Resettables; | ||
|
|
||
| namespace Nethermind.Core.Extensions; | ||
|
|
||
| public static class DictionaryExtensions | ||
| { | ||
| /// <summary> | ||
| /// Returns all values in the dictionary to their pool by calling <see cref="IReturnable.Return"/> on each value, | ||
| /// then clears the dictionary. | ||
| /// </summary> | ||
| /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> | ||
| /// <typeparam name="TValue">The type of the values in the dictionary, which must implement <see cref="IReturnable"/>.</typeparam> | ||
| /// <param name="dictionary">The dictionary whose values will be returned and cleared.</param> | ||
| /// <remarks> | ||
| /// Use this method when you need to both return pooled objects and clear the dictionary in one operation. | ||
| /// </remarks> | ||
| public static void ResetAndClear<TKey, TValue>(this IDictionary<TKey, TValue> dictionary) | ||
| where TValue : class, IReturnable | ||
| { | ||
| foreach (TValue value in dictionary.Values) | ||
| { | ||
| value.Return(); | ||
| } | ||
| dictionary.Clear(); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited | ||
| // SPDX-License-Identifier: LGPL-3.0-only | ||
|
|
||
| namespace Nethermind.Core.Resettables; | ||
|
|
||
| /// <summary> | ||
| /// Defines a contract for objects that can be reset to their initial state. | ||
| /// </summary> | ||
| public interface IResettable | ||
| { | ||
| /// <summary> | ||
| /// Resets the object to its initial state. | ||
| /// </summary> | ||
| void Reset(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited | ||
| // SPDX-License-Identifier: LGPL-3.0-only | ||
|
|
||
| namespace Nethermind.Core.Resettables; | ||
|
|
||
| /// <summary> | ||
| /// Defines a contract for objects that can be returned to a pool or resettable resource manager. | ||
| /// Implementations should ensure that <see cref="Return"/> releases or resets the object for reuse. | ||
| /// </summary> | ||
| public interface IReturnable | ||
| { | ||
| /// <summary> | ||
| /// Returns the object to its pool or resource manager, making it available for reuse. | ||
| /// Implementations should ensure the object is properly reset or cleaned up. | ||
| /// </summary> | ||
| void Return(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.