From 679043c327ff5cb343c9cbd89f79c681592dcbc0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 02:25:38 +0100 Subject: [PATCH 1/8] Less allocations for node processing --- .../Lifecycle/NodeLifecycleManager.cs | 14 +- .../NodesLocator.cs | 50 +++++-- .../RoutingTable/INodeTable.cs | 6 +- .../RoutingTable/NodeBucket.cs | 62 ++++++-- .../RoutingTable/NodeTable.cs | 135 +++++++++++++++--- 5 files changed, 222 insertions(+), 45 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/Lifecycle/NodeLifecycleManager.cs b/src/Nethermind/Nethermind.Network.Discovery/Lifecycle/NodeLifecycleManager.cs index 20e7d79c854..b7c9e618a83 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/Lifecycle/NodeLifecycleManager.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/Lifecycle/NodeLifecycleManager.cs @@ -212,10 +212,16 @@ public void ProcessFindNodeMsg(FindNodeMsg msg) NodeStats.AddNodeStatsEvent(NodeStatsEventType.DiscoveryFindNodeIn); RefreshNodeContactTime(); - Node[] nodes = _nodeTable - .GetClosestNodes(msg.SearchedNodeId) - .Take(12) // Otherwise the payload may become too big, which is out of spec. - .ToArray(); + // 12 otherwise the payload may become too big, which is out of spec. + var closestNodes = _nodeTable.GetClosestNodes(msg.SearchedNodeId, bucketSize: 12); + Node[] nodes = new Node[closestNodes.Count]; + int count = 0; + foreach (Node node in closestNodes) + { + nodes[count] = node; + count++; + } + SendNeighbors(nodes); } diff --git a/src/Nethermind/Nethermind.Network.Discovery/NodesLocator.cs b/src/Nethermind/Nethermind.Network.Discovery/NodesLocator.cs index 63c33df2ec5..d26de376adb 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/NodesLocator.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/NodesLocator.cs @@ -33,9 +33,9 @@ public void Initialize(Node masterNode) _masterNode = masterNode; } - public async Task LocateNodesAsync(CancellationToken cancellationToken) + public Task LocateNodesAsync(CancellationToken cancellationToken) { - await LocateNodesAsync(null, cancellationToken); + return LocateNodesAsync(null, cancellationToken); } public async Task LocateNodesAsync(byte[]? searchedNodeId, CancellationToken cancellationToken) @@ -59,16 +59,37 @@ public async Task LocateNodesAsync(byte[]? searchedNodeId, CancellationToken can int attemptsCount = 0; while (true) { - //if searched node is not specified master node is used - IEnumerable closestNodes = searchedNodeId is not null ? _nodeTable.GetClosestNodes(searchedNodeId) : _nodeTable.GetClosestNodes(); - candidatesCount = 0; - foreach (Node closestNode in closestNodes.Where(node => !alreadyTriedNodes.Contains(node.IdHash))) + if (searchedNodeId is not null) + { + foreach (Node closestNode in _nodeTable.GetClosestNodes(searchedNodeId)) + { + if (alreadyTriedNodes.Contains(closestNode.IdHash)) + { + continue; + } + + tryCandidates[candidatesCount++] = closestNode; + if (candidatesCount > tryCandidates.Length - 1) + { + break; + } + } + } + else { - tryCandidates[candidatesCount++] = closestNode; - if (candidatesCount > tryCandidates.Length - 1) + foreach (Node closestNode in _nodeTable.GetClosestNodes()) { - break; + if (alreadyTriedNodes.Contains(closestNode.IdHash)) + { + continue; + } + + tryCandidates[candidatesCount++] = closestNode; + if (candidatesCount > tryCandidates.Length - 1) + { + break; + } } } @@ -134,7 +155,14 @@ public async Task LocateNodesAsync(byte[]? searchedNodeId, CancellationToken can } } } - int nodesCountAfterDiscovery = _nodeTable.Buckets.Sum(x => x.BondedItemsCount); + + int nodesCountAfterDiscovery = 0; + var buckets = _nodeTable.Buckets; + for (int i = 0; i < buckets.Length; i++) + { + nodesCountAfterDiscovery += buckets[i].BondedItemsCount; + } + if (_logger.IsDebug) _logger.Debug($"Finished discovery cycle, tried contacting {alreadyTriedNodes.Count} nodes. All nodes count before the process: {nodesCountBeforeDiscovery}, after the process: {nodesCountAfterDiscovery}"); if (_logger.IsTrace) @@ -172,7 +200,7 @@ private int NodesCountBeforeDiscovery private void LogNodeTable() { - IEnumerable nonEmptyBuckets = _nodeTable.Buckets.Where(x => x.BondedItems.Any()); + IEnumerable nonEmptyBuckets = _nodeTable.Buckets.Where(x => x.AnyBondedItems()); StringBuilder sb = new(); int length = 0; diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/INodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/INodeTable.cs index a5fe2475504..da0749b8b33 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/INodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/INodeTable.cs @@ -3,6 +3,7 @@ using Nethermind.Core.Crypto; using Nethermind.Stats.Model; +using static Nethermind.Network.Discovery.RoutingTable.NodeTable; namespace Nethermind.Network.Discovery.RoutingTable; @@ -18,10 +19,11 @@ public interface INodeTable /// /// GetClosestNodes to MasterNode /// - IEnumerable GetClosestNodes(); + ClosestNodesEnumerator GetClosestNodes(); /// /// GetClosestNodes to provided Node /// - IEnumerable GetClosestNodes(byte[] nodeId); + ClosestNodesFromNodeEnumerator GetClosestNodes(byte[] nodeId); + ClosestNodesFromNodeEnumerator GetClosestNodes(byte[] nodeId, int bucketSize); } diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs index f00be7b4a90..5c128c10ba2 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Collections; using System.Diagnostics; using Nethermind.Stats.Model; @@ -28,26 +29,69 @@ public NodeBucket(int distance, int bucketSize, float dropFullBucketProbability public int BucketSize { get; } - public IEnumerable BondedItems + public bool AnyBondedItems() { - get + foreach (NodeBucketItem _ in BondedItems) { - lock (_nodeBucketLock) + return true; + } + + return false; + } + + public BondedItemsEnumerator BondedItems + => new(this); + + public struct BondedItemsEnumerator : IEnumerator, IEnumerable + { + private NodeBucket _nodeBucket; + private LinkedListNode? _currentNode; + private DateTime _referenceTime; + + public BondedItemsEnumerator(NodeBucket nodeBucket) + { + _nodeBucket = nodeBucket; + _referenceTime = DateTime.UtcNow; + lock (_nodeBucket._nodeBucketLock) { - LinkedListNode? node = _items.Last; - DateTime utcNow = DateTime.UtcNow; - while (node is not null) + _currentNode = nodeBucket._items.Last; + } + Current = default!; + } + + public NodeBucketItem Current { get; private set; } + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + lock (_nodeBucket._nodeBucketLock) + { + while (_currentNode != null) { - if (!node.Value.IsBonded(utcNow)) + if (!_currentNode.Value.IsBonded(_referenceTime)) { break; } - yield return node.Value; - node = node.Previous; + Current = _currentNode.Value; + _currentNode = _currentNode.Previous; + return true; } } + return false; } + + public void Reset() => throw new NotSupportedException(); + + public readonly void Dispose() { } + public BondedItemsEnumerator GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); } public int BondedItemsCount diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index adc629d9302..9d31b56f8a1 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -1,10 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Collections; +using System.Runtime.InteropServices; using Nethermind.Core.Crypto; using Nethermind.Logging; using Nethermind.Network.Config; using Nethermind.Stats.Model; +using static Nethermind.Network.Discovery.RoutingTable.NodeBucket; namespace Nethermind.Network.Discovery.RoutingTable; @@ -73,42 +76,136 @@ public void RefreshNode(Node node) bucket.RefreshNode(node); } - public IEnumerable GetClosestNodes() + public ClosestNodesEnumerator GetClosestNodes() { - int count = 0; - int bucketSize = _discoveryConfig.BucketSize; + return new ClosestNodesEnumerator(Buckets, _discoveryConfig.BucketSize); + } + + public struct ClosestNodesEnumerator : IEnumerator, IEnumerable + { + private readonly NodeBucket[] _buckets; + private readonly int _bucketSize; + private BondedItemsEnumerator? _itemEnumerator; + private int _bucketIndex; + private int _count; + + public ClosestNodesEnumerator(NodeBucket[] buckets, int bucketSize) + { + _buckets = buckets; + _bucketSize = bucketSize; + _itemEnumerator = null; + Current = null!; + _bucketIndex = -1; + _count = 0; + } - foreach (NodeBucket nodeBucket in Buckets) + public Node Current { get; private set; } + + object IEnumerator.Current => Current; + + public bool MoveNext() { - foreach (NodeBucketItem nodeBucketItem in nodeBucket.BondedItems) + while (_count < _bucketSize) { - if (count < bucketSize) + if (_itemEnumerator == null || !_itemEnumerator.Value.MoveNext()) { - count++; - if (nodeBucketItem.Node is not null) + _bucketIndex++; + if (_bucketIndex >= _buckets.Length) { - yield return nodeBucketItem.Node; + return false; } + + _itemEnumerator = _buckets[_bucketIndex].BondedItems.GetEnumerator(); + continue; } - else + + if (_itemEnumerator.Value.Current?.Node is not null) { - yield break; + Current = _itemEnumerator.Value.Current.Node; + _count++; + return true; } } + return false; } + + public void Reset() => throw new NotSupportedException(); + + public void Dispose() + { + _itemEnumerator?.Dispose(); + } + + public ClosestNodesEnumerator GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; } - public IEnumerable GetClosestNodes(byte[] nodeId) + public ClosestNodesFromNodeEnumerator GetClosestNodes(byte[] nodeId) + { + return GetClosestNodes(nodeId, _discoveryConfig.BucketSize); + } + + public ClosestNodesFromNodeEnumerator GetClosestNodes(byte[] nodeId, int bucketSize) { CheckInitialization(); + return new ClosestNodesFromNodeEnumerator(Buckets, nodeId, _nodeDistanceCalculator, Math.Min(bucketSize, _discoveryConfig.BucketSize)); + } + + public struct ClosestNodesFromNodeEnumerator : IEnumerator, IEnumerable + { + private readonly List _sortedNodes; + private int _currentIndex; + + public ClosestNodesFromNodeEnumerator(NodeBucket[] buckets, byte[] targetNodeId, INodeDistanceCalculator calculator, int bucketSize) + { + _sortedNodes = new List(); + Hash256 idHash = Keccak.Compute(targetNodeId); + foreach (var bucket in buckets) + { + foreach (var item in bucket.BondedItems) + { + if (item.Node != null && item.Node.IdHash != idHash) + { + _sortedNodes.Add(item.Node); + } + } + } + + _sortedNodes.Sort((a, b) => calculator.CalculateDistance(a.Id.Bytes, targetNodeId).CompareTo(calculator.CalculateDistance(b.Id.Bytes, targetNodeId))); + if (_sortedNodes.Count > bucketSize) + { + CollectionsMarshal.SetCount(_sortedNodes, bucketSize); + } + + _currentIndex = -1; + } + + public readonly int Count => _sortedNodes.Count; + + public Node Current => _sortedNodes[_currentIndex]; + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + if (_currentIndex + 1 < _sortedNodes.Count) + { + _currentIndex++; + return true; + } + return false; + } + + public void Reset() => throw new NotSupportedException(); + public void Dispose() { } + + public ClosestNodesFromNodeEnumerator GetEnumerator() => this; + IEnumerator IEnumerable.GetEnumerator() => this; - Hash256 idHash = Keccak.Compute(nodeId); - return Buckets.SelectMany(x => x.BondedItems) - .Where(x => x.Node?.IdHash != idHash && x.Node is not null) - .Select(x => new { x.Node, Distance = _nodeDistanceCalculator.CalculateDistance(x.Node!.Id.Bytes, nodeId) }) - .OrderBy(x => x.Distance) - .Take(_discoveryConfig.BucketSize) - .Select(x => x.Node!); + IEnumerator IEnumerable.GetEnumerator() => this; } public void Initialize(PublicKey masterNodeKey) From fa23186f0fa421f9512a3cee998bb084154932ed Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 02:54:20 +0100 Subject: [PATCH 2/8] Can't use nullable with mutable struct --- .../RoutingTable/NodeBucket.cs | 12 +++++------- .../RoutingTable/NodeTable.cs | 16 +++++++--------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs index 5c128c10ba2..7ac95735dc7 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs @@ -67,16 +67,14 @@ public bool MoveNext() { lock (_nodeBucket._nodeBucketLock) { - while (_currentNode != null) + while (_currentNode is not null) { - if (!_currentNode.Value.IsBonded(_referenceTime)) - { - break; - } - Current = _currentNode.Value; _currentNode = _currentNode.Previous; - return true; + if (Current.IsBonded(_referenceTime)) + { + return true; + } } } return false; diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index 9d31b56f8a1..029f7e343f5 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -85,7 +85,8 @@ public struct ClosestNodesEnumerator : IEnumerator, IEnumerable { private readonly NodeBucket[] _buckets; private readonly int _bucketSize; - private BondedItemsEnumerator? _itemEnumerator; + private BondedItemsEnumerator _itemEnumerator; + private bool _enumeratorSet; private int _bucketIndex; private int _count; @@ -93,7 +94,6 @@ public ClosestNodesEnumerator(NodeBucket[] buckets, int bucketSize) { _buckets = buckets; _bucketSize = bucketSize; - _itemEnumerator = null; Current = null!; _bucketIndex = -1; _count = 0; @@ -107,7 +107,7 @@ public bool MoveNext() { while (_count < _bucketSize) { - if (_itemEnumerator == null || !_itemEnumerator.Value.MoveNext()) + if (!_enumeratorSet || !_itemEnumerator.MoveNext()) { _bucketIndex++; if (_bucketIndex >= _buckets.Length) @@ -116,12 +116,13 @@ public bool MoveNext() } _itemEnumerator = _buckets[_bucketIndex].BondedItems.GetEnumerator(); + _enumeratorSet = true; continue; } - if (_itemEnumerator.Value.Current?.Node is not null) + if (_itemEnumerator.Current.Node is not null) { - Current = _itemEnumerator.Value.Current.Node; + Current = _itemEnumerator.Current.Node; _count++; return true; } @@ -131,10 +132,7 @@ public bool MoveNext() public void Reset() => throw new NotSupportedException(); - public void Dispose() - { - _itemEnumerator?.Dispose(); - } + public void Dispose() { } public ClosestNodesEnumerator GetEnumerator() => this; From 9b7775b19e7d0aca638e5bde80b1ce6d51cb62c8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 03:08:02 +0100 Subject: [PATCH 3/8] Tweak --- .../RoutingTable/NodeBucket.cs | 4 +++- .../RoutingTable/NodeTable.cs | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs index 7ac95735dc7..416a9e1911c 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs @@ -56,7 +56,7 @@ public BondedItemsEnumerator(NodeBucket nodeBucket) { _currentNode = nodeBucket._items.Last; } - Current = default!; + Current = null!; } public NodeBucketItem Current { get; private set; } @@ -77,6 +77,8 @@ public bool MoveNext() } } } + + Current = null!; return false; } diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index 029f7e343f5..cc31d0c283f 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -120,12 +120,9 @@ public bool MoveNext() continue; } - if (_itemEnumerator.Current.Node is not null) - { - Current = _itemEnumerator.Current.Node; - _count++; - return true; - } + Current = _itemEnumerator.Current.Node!; + _count++; + return true; } return false; } From dd4dcc5e696938df68fa4d111d4a65101091d728 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 03:41:57 +0100 Subject: [PATCH 4/8] Hold lock --- .../RoutingTable/NodeBucket.cs | 28 ++++++++++--------- .../RoutingTable/NodeTable.cs | 3 ++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs index 416a9e1911c..583aae75fd1 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs @@ -52,10 +52,8 @@ public BondedItemsEnumerator(NodeBucket nodeBucket) { _nodeBucket = nodeBucket; _referenceTime = DateTime.UtcNow; - lock (_nodeBucket._nodeBucketLock) - { - _currentNode = nodeBucket._items.Last; - } + Monitor.Enter(_nodeBucket._nodeBucketLock); + _currentNode = nodeBucket._items.Last; Current = null!; } @@ -65,16 +63,13 @@ public BondedItemsEnumerator(NodeBucket nodeBucket) public bool MoveNext() { - lock (_nodeBucket._nodeBucketLock) + while (_currentNode is not null) { - while (_currentNode is not null) + Current = _currentNode.Value; + _currentNode = _currentNode.Previous; + if (Current.IsBonded(_referenceTime)) { - Current = _currentNode.Value; - _currentNode = _currentNode.Previous; - if (Current.IsBonded(_referenceTime)) - { - return true; - } + return true; } } @@ -84,7 +79,14 @@ public bool MoveNext() public void Reset() => throw new NotSupportedException(); - public readonly void Dispose() { } + public void Dispose() + { + if (_nodeBucket is not null) + { + Monitor.Exit(_nodeBucket._nodeBucketLock); + } + _nodeBucket = null!; + } public BondedItemsEnumerator GetEnumerator() => this; IEnumerator IEnumerable.GetEnumerator() diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index cc31d0c283f..f0ef0d39e93 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -109,6 +109,7 @@ public bool MoveNext() { if (!_enumeratorSet || !_itemEnumerator.MoveNext()) { + _itemEnumerator.Dispose(); _bucketIndex++; if (_bucketIndex >= _buckets.Length) { @@ -124,6 +125,8 @@ public bool MoveNext() _count++; return true; } + + _itemEnumerator.Dispose(); return false; } From 6f26205924edd81994bce39d8fd3d156c12efa14 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 03:44:13 +0100 Subject: [PATCH 5/8] Dispose in finally --- .../RoutingTable/NodeTable.cs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index f0ef0d39e93..9aa8b02a7c3 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -105,28 +105,34 @@ public ClosestNodesEnumerator(NodeBucket[] buckets, int bucketSize) public bool MoveNext() { - while (_count < _bucketSize) + try { - if (!_enumeratorSet || !_itemEnumerator.MoveNext()) + while (_count < _bucketSize) { - _itemEnumerator.Dispose(); - _bucketIndex++; - if (_bucketIndex >= _buckets.Length) + if (!_enumeratorSet || !_itemEnumerator.MoveNext()) { - return false; + _itemEnumerator.Dispose(); + _bucketIndex++; + if (_bucketIndex >= _buckets.Length) + { + return false; + } + + _itemEnumerator = _buckets[_bucketIndex].BondedItems.GetEnumerator(); + _enumeratorSet = true; + continue; } - _itemEnumerator = _buckets[_bucketIndex].BondedItems.GetEnumerator(); - _enumeratorSet = true; - continue; + Current = _itemEnumerator.Current.Node!; + _count++; + return true; } - - Current = _itemEnumerator.Current.Node!; - _count++; - return true; + } + finally + { + _itemEnumerator.Dispose(); } - _itemEnumerator.Dispose(); return false; } From e2586909ec940bb9271620645abedd6ddfe058a9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 10:53:05 +0100 Subject: [PATCH 6/8] Hide reset behind explicit implementation --- .../Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs | 2 +- .../Nethermind.Network.Discovery/RoutingTable/NodeTable.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs index 583aae75fd1..e0a5445a4fa 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeBucket.cs @@ -77,7 +77,7 @@ public bool MoveNext() return false; } - public void Reset() => throw new NotSupportedException(); + void IEnumerator.Reset() => throw new NotSupportedException(); public void Dispose() { diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index 9aa8b02a7c3..7b4ed3c4be8 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -136,7 +136,7 @@ public bool MoveNext() return false; } - public void Reset() => throw new NotSupportedException(); + void IEnumerator.Reset() => throw new NotSupportedException(); public void Dispose() { } @@ -203,7 +203,7 @@ public bool MoveNext() return false; } - public void Reset() => throw new NotSupportedException(); + void IEnumerator.Reset() => throw new NotSupportedException(); public void Dispose() { } public ClosestNodesFromNodeEnumerator GetEnumerator() => this; From a144110a4b712760e32dabac8f9c60e2a758b9d2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 11:25:03 +0100 Subject: [PATCH 7/8] Use ArrayPoolList --- .../Collections/ArrayPoolList.cs | 44 ++++++++++++++++++- .../RoutingTable/NodeTable.cs | 13 +++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index c284e060f7e..be23dfa6c9e 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading; @@ -121,6 +120,49 @@ void ICollection.CopyTo(Array array, int index) } public int Count { get; private set; } = 0; + public void ReduceCount(int count) + { + GuardDispose(); + var oldCount = Count; + if (count == oldCount) return; + + if (count > oldCount) + { + ThrowOnlyReduce(count); + } + + Count = count; + if (count < _capacity / 2) + { + // Reduced to less than half of the capacity, resize the array. + T[] newArray = _arrayPool.Rent(count); + _array.AsSpan(0, count).CopyTo(newArray); + T[] oldArray = Interlocked.Exchange(ref _array, newArray); + _capacity = newArray.Length; + _arrayPool.Return(oldArray); + } + else if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + // Release any references to the objects in the array that are no longer in use. + Array.Clear(_array, count, oldCount); + } + + void ThrowOnlyReduce(int count) + { + throw new ArgumentException($"Count can only be reduced. {count} is larger than {Count}", nameof(count)); + } + } + + public void Sort(Comparison comparison) + { + ArgumentNullException.ThrowIfNull(comparison); + GuardDispose(); + + if (Count > 1) + { + _array.AsSpan(0, Count).Sort(comparison); + } + } public int Capacity => _capacity; diff --git a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs index 7b4ed3c4be8..fbd597224ac 100644 --- a/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs +++ b/src/Nethermind/Nethermind.Network.Discovery/RoutingTable/NodeTable.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections; -using System.Runtime.InteropServices; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Logging; using Nethermind.Network.Config; @@ -160,12 +160,12 @@ public ClosestNodesFromNodeEnumerator GetClosestNodes(byte[] nodeId, int bucketS public struct ClosestNodesFromNodeEnumerator : IEnumerator, IEnumerable { - private readonly List _sortedNodes; + private readonly ArrayPoolList _sortedNodes; private int _currentIndex; public ClosestNodesFromNodeEnumerator(NodeBucket[] buckets, byte[] targetNodeId, INodeDistanceCalculator calculator, int bucketSize) { - _sortedNodes = new List(); + _sortedNodes = new ArrayPoolList(capacity: bucketSize); Hash256 idHash = Keccak.Compute(targetNodeId); foreach (var bucket in buckets) { @@ -181,7 +181,7 @@ public ClosestNodesFromNodeEnumerator(NodeBucket[] buckets, byte[] targetNodeId, _sortedNodes.Sort((a, b) => calculator.CalculateDistance(a.Id.Bytes, targetNodeId).CompareTo(calculator.CalculateDistance(b.Id.Bytes, targetNodeId))); if (_sortedNodes.Count > bucketSize) { - CollectionsMarshal.SetCount(_sortedNodes, bucketSize); + _sortedNodes.ReduceCount(bucketSize); } _currentIndex = -1; @@ -204,7 +204,10 @@ public bool MoveNext() } void IEnumerator.Reset() => throw new NotSupportedException(); - public void Dispose() { } + public void Dispose() + { + _sortedNodes.Dispose(); + } public ClosestNodesFromNodeEnumerator GetEnumerator() => this; IEnumerator IEnumerable.GetEnumerator() => this; From b7ae19e437c0be573c7ee0b4c7a36ea1117ca6e1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 2 May 2024 11:28:19 +0100 Subject: [PATCH 8/8] Add back namsepace --- src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index be23dfa6c9e..69ae3eab2f6 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading;