Skip to content

Commit 7252943

Browse files
authored
Shrink Trienodes (#7915)
1 parent 6be3c46 commit 7252943

File tree

13 files changed

+455
-306
lines changed

13 files changed

+455
-306
lines changed

src/Nethermind/Nethermind.Core/Crypto/Hash256.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ public sealed class Hash256 : IEquatable<Hash256>, IComparable<Hash256>
144144
public static readonly Hash256 Zero = new("0x0000000000000000000000000000000000000000000000000000000000000000");
145145

146146
public const int MemorySize =
147-
MemorySizes.SmallObjectOverhead -
148-
MemorySizes.RefSize +
147+
MemorySizes.ObjectHeaderMethodTable +
149148
Size;
150149

151150
private readonly ValueHash256 _hash256;

src/Nethermind/Nethermind.Core/MemorySizes.cs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static long Align(long unalignedSize)
2020

2121
public const int RefSize = 8;
2222

23+
public const int ObjectHeaderMethodTable = 16;
2324
public const int SmallObjectOverhead = 24;
2425

2526
public const int SmallObjectFreeDataSize = 8;

src/Nethermind/Nethermind.Serialization.Rlp/RlpFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace Nethermind.Serialization.Rlp
88
{
99
public class RlpFactory
1010
{
11-
public long MemorySize => MemorySizes.SmallObjectOverhead
11+
public long MemorySize => MemorySizes.ObjectHeaderMethodTable
1212
+ MemorySizes.Align(MemorySizes.ArrayOverhead + _data.Length)
13-
+ MemorySizes.Align(sizeof(int));
13+
+ MemorySizes.Align(MemorySizes.RefSize + sizeof(int));
1414

1515
private readonly CappedArray<byte> _data;
1616

src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,6 @@ public byte[][] DecodeByteArrays()
14791479
}
14801480

14811481
internal static ReadOnlySpan<byte> SingleBytes => new byte[128] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 };
1482-
internal static byte[][] SingleByteArrays = new byte[128][] { [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31], [32], [33], [34], [35], [36], [37], [38], [39], [40], [41], [42], [43], [44], [45], [46], [47], [48], [49], [50], [51], [52], [53], [54], [55], [56], [57], [58], [59], [60], [61], [62], [63], [64], [65], [66], [67], [68], [69], [70], [71], [72], [73], [74], [75], [76], [77], [78], [79], [80], [81], [82], [83], [84], [85], [86], [87], [88], [89], [90], [91], [92], [93], [94], [95], [96], [97], [98], [99], [100], [101], [102], [103], [104], [105], [106], [107], [108], [109], [110], [111], [112], [113], [114], [115], [116], [117], [118], [119], [120], [121], [122], [123], [124], [125], [126], [127] };
1482+
internal static readonly byte[][] SingleByteArrays = new byte[128][] { [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31], [32], [33], [34], [35], [36], [37], [38], [39], [40], [41], [42], [43], [44], [45], [46], [47], [48], [49], [50], [51], [52], [53], [54], [55], [56], [57], [58], [59], [60], [61], [62], [63], [64], [65], [66], [67], [68], [69], [70], [71], [72], [73], [74], [75], [76], [77], [78], [79], [80], [81], [82], [83], [84], [85], [86], [87], [88], [89], [90], [91], [92], [93], [94], [95], [96], [97], [98], [99], [100], [101], [102], [103], [104], [105], [106], [107], [108], [109], [110], [111], [112], [113], [114], [115], [116], [117], [118], [119], [120], [121], [122], [123], [124], [125], [126], [127] };
14831483
}
14841484
}

src/Nethermind/Nethermind.Synchronization/SnapSync/SnapProviderHelper.cs

+17-10
Original file line numberDiff line numberDiff line change
@@ -311,23 +311,26 @@ private static void StitchBoundaries(List<(TrieNode, TreePath)> sortedBoundaryLi
311311

312312
if (!node.IsPersisted)
313313
{
314-
if (node.IsExtension)
314+
INodeData nodeData = node.NodeData;
315+
if (nodeData is ExtensionData extensionData)
315316
{
316-
if (IsChildPersisted(node, ref path, ExtensionRlpChildIndex, store))
317+
if (IsChildPersisted(node, ref path, extensionData._value, ExtensionRlpChildIndex, store))
317318
{
318319
node.IsBoundaryProofNode = false;
319320
}
320321
}
321-
else if (node.IsBranch)
322+
else if (nodeData is BranchData branchData)
322323
{
323324
bool isBoundaryProofNode = false;
324-
for (int ci = 0; ci <= 15; ci++)
325+
int ci = 0;
326+
foreach (object? o in branchData.Branches)
325327
{
326-
if (!IsChildPersisted(node, ref path, ci, store))
328+
if (!IsChildPersisted(node, ref path, o, ci, store))
327329
{
328330
isBoundaryProofNode = true;
329331
break;
330332
}
333+
ci++;
331334
}
332335

333336
node.IsBoundaryProofNode = isBoundaryProofNode;
@@ -336,15 +339,19 @@ private static void StitchBoundaries(List<(TrieNode, TreePath)> sortedBoundaryLi
336339
}
337340
}
338341

339-
private static bool IsChildPersisted(TrieNode node, ref TreePath nodePath, int childIndex, IScopedTrieStore store)
342+
private static bool IsChildPersisted(TrieNode node, ref TreePath nodePath, object? child, int childIndex, IScopedTrieStore store)
340343
{
341-
TrieNode data = node.GetData(childIndex) as TrieNode;
342-
if (data is not null)
344+
if (child is TrieNode childNode)
343345
{
344-
return data.IsBoundaryProofNode == false;
346+
return childNode.IsBoundaryProofNode == false;
345347
}
346348

347-
if (!node.GetChildHashAsValueKeccak(childIndex, out ValueHash256 childKeccak))
349+
ValueHash256 childKeccak;
350+
if (child is Hash256 hash)
351+
{
352+
childKeccak = hash.ValueHash256;
353+
}
354+
else if (!node.GetChildHashAsValueKeccak(childIndex, out childKeccak))
348355
{
349356
return true;
350357
}

src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public void FindCachedOrUnknown_CorrectlyCalculatedMemoryUsedByDirtyCache()
198198
long startSize = trieStore.MemoryUsedByDirtyCache;
199199
trieStore.FindCachedOrUnknown(null, TreePath.Empty, TestItem.KeccakA);
200200
TrieNode trieNode = new(NodeType.Leaf, Keccak.Zero);
201-
long oneKeccakSize = trieNode.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize;
201+
long oneKeccakSize = trieNode.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize - MemorySizes.SmallObjectOverhead;
202202
Assert.That(trieStore.MemoryUsedByDirtyCache, Is.EqualTo(startSize + oneKeccakSize));
203203
trieStore.FindCachedOrUnknown(null, TreePath.Empty, TestItem.KeccakB);
204204
Assert.That(trieStore.MemoryUsedByDirtyCache, Is.EqualTo(2 * oneKeccakSize + startSize));
@@ -253,7 +253,7 @@ public void Memory_with_concurrent_commits_is_correct()
253253
tree.Commit();
254254
}
255255

256-
fullTrieStore.MemoryUsedByDirtyCache.Should().Be(_scheme == INodeStorage.KeyScheme.Hash ? 591672L : 661820L);
256+
fullTrieStore.MemoryUsedByDirtyCache.Should().Be(_scheme == INodeStorage.KeyScheme.Hash ? 540560L : 610708L);
257257
fullTrieStore.CommittedNodesCount.Should().Be(1349);
258258
}
259259

@@ -1017,7 +1017,7 @@ public async Task Will_Not_RemovePastKeys_OnSnapshot_DuringFullPruning()
10171017

10181018
memDb.Count.Should().Be(61);
10191019
fullTrieStore.Prune();
1020-
fullTrieStore.MemoryUsedByDirtyCache.Should().Be(_scheme == INodeStorage.KeyScheme.Hash ? 504 : 660);
1020+
fullTrieStore.MemoryUsedByDirtyCache.Should().Be(_scheme == INodeStorage.KeyScheme.Hash ? 552 : 708);
10211021
}
10221022

10231023
[Test]

src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs

+15-16
Original file line numberDiff line numberDiff line change
@@ -548,29 +548,28 @@ public void Can_encode_branch_with_unresolved_children()
548548
public void Size_of_a_heavy_leaf_is_correct()
549549
{
550550
Context ctx = new();
551-
Assert.That(ctx.HeavyLeaf.GetMemorySize(false), Is.EqualTo(248));
551+
Assert.That(ctx.HeavyLeaf.GetMemorySize(false), Is.EqualTo(208));
552552
}
553553

554554
[Test]
555555
public void Size_of_a_tiny_leaf_is_correct()
556556
{
557557
Context ctx = new();
558-
Assert.That(ctx.TiniestLeaf.GetMemorySize(false), Is.EqualTo(168));
558+
Assert.That(ctx.TiniestLeaf.GetMemorySize(false), Is.EqualTo(136));
559559
}
560560

561561
[Test]
562562
public void Size_of_a_branch_is_correct()
563563
{
564564
Context ctx = new();
565565
TrieNode node = new(NodeType.Branch);
566-
node.Key = new byte[] { 1 };
567566
for (int i = 0; i < 16; i++)
568567
{
569568
node.SetChild(i, ctx.AccountLeaf);
570569
}
571570

572-
Assert.That(node.GetMemorySize(true), Is.EqualTo(4048));
573-
Assert.That(node.GetMemorySize(false), Is.EqualTo(208));
571+
Assert.That(node.GetMemorySize(true), Is.EqualTo(3376));
572+
Assert.That(node.GetMemorySize(false), Is.EqualTo(176));
574573
}
575574

576575
[Test]
@@ -581,7 +580,7 @@ public void Size_of_an_extension_is_correct()
581580
trieNode.Key = new byte[] { 1 };
582581
trieNode.SetChild(0, ctx.TiniestLeaf);
583582

584-
Assert.That(trieNode.GetMemorySize(false), Is.EqualTo(120));
583+
Assert.That(trieNode.GetMemorySize(false), Is.EqualTo(96));
585584
}
586585

587586
[Test]
@@ -592,53 +591,53 @@ public void Size_of_unknown_node_is_correct()
592591
trieNode.Key = new byte[] { 1 };
593592
trieNode.SetChild(0, ctx.TiniestLeaf);
594593

595-
Assert.That(trieNode.GetMemorySize(true), Is.EqualTo(288));
596-
Assert.That(trieNode.GetMemorySize(false), Is.EqualTo(120));
594+
Assert.That(trieNode.GetMemorySize(true), Is.EqualTo(232));
595+
Assert.That(trieNode.GetMemorySize(false), Is.EqualTo(96));
597596
}
598597

599598
[Test]
600599
public void Size_of_an_unknown_empty_node_is_correct()
601600
{
602601
TrieNode trieNode = new(NodeType.Unknown);
603-
trieNode.GetMemorySize(false).Should().Be(56);
602+
trieNode.GetMemorySize(false).Should().Be(48);
604603
}
605604

606605
[Test]
607606
public void Size_of_an_unknown_node_with_keccak_is_correct()
608607
{
609608
TrieNode trieNode = new(NodeType.Unknown, Keccak.Zero);
610-
trieNode.GetMemorySize(false).Should().Be(104);
609+
trieNode.GetMemorySize(false).Should().Be(96);
611610
}
612611

613612
[Test]
614613
public void Size_of_extension_with_child()
615614
{
616615
TrieNode trieNode = new(NodeType.Extension);
617616
trieNode.SetChild(0, null);
618-
trieNode.GetMemorySize(false).Should().Be(96);
617+
trieNode.GetMemorySize(false).Should().Be(64);
619618
}
620619

621620
[Test]
622621
public void Size_of_branch_with_data()
623622
{
624623
TrieNode trieNode = new(NodeType.Branch);
625624
trieNode.SetChild(0, null);
626-
trieNode.GetMemorySize(false).Should().Be(208);
625+
trieNode.GetMemorySize(false).Should().Be(176);
627626
}
628627

629628
[Test]
630629
public void Size_of_leaf_with_value()
631630
{
632631
TrieNode trieNode = new(NodeType.Leaf);
633632
trieNode.Value = new byte[7];
634-
trieNode.GetMemorySize(false).Should().Be(152);
633+
trieNode.GetMemorySize(false).Should().Be(104);
635634
}
636635

637636
[Test]
638637
public void Size_of_an_unknown_node_with_full_rlp_is_correct()
639638
{
640639
TrieNode trieNode = new(NodeType.Unknown, new byte[7]);
641-
trieNode.GetMemorySize(false).Should().Be(120);
640+
trieNode.GetMemorySize(false).Should().Be(112);
642641
}
643642

644643
[Test]
@@ -738,7 +737,7 @@ public void Extension_child_as_keccak_memory_size()
738737
trieNode.SetChild(0, child);
739738

740739
trieNode.PrunePersistedRecursively(1);
741-
trieNode.GetMemorySize(false).Should().Be(144);
740+
trieNode.GetMemorySize(false).Should().Be(112);
742741
}
743742

744743
[Test]
@@ -751,7 +750,7 @@ public void Extension_child_as_keccak_clone()
751750
trieNode.PrunePersistedRecursively(1);
752751
TrieNode cloned = trieNode.Clone();
753752

754-
cloned.GetMemorySize(false).Should().Be(144);
753+
cloned.GetMemorySize(false).Should().Be(112);
755754
}
756755

757756
[Test]

src/Nethermind/Nethermind.Trie/HexPrefix.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static void CopyToSpan(byte[] path, bool isLeaf, Span<byte> output)
1616
{
1717
if (output.Length != ByteLength(path)) throw new ArgumentOutOfRangeException(nameof(output));
1818

19-
output[0] = (byte)(isLeaf ? 0x20 : 0x000);
19+
output[0] = (byte)(isLeaf ? 0x20 : 0x00);
2020
if (path.Length % 2 != 0)
2121
{
2222
output[0] += (byte)(0x10 + path[0]);
+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using Nethermind.Core.Buffers;
7+
using Nethermind.Core;
8+
using System.Diagnostics.CodeAnalysis;
9+
using System.Diagnostics;
10+
11+
namespace Nethermind.Trie;
12+
13+
public interface INodeData
14+
{
15+
public NodeType NodeType { get; }
16+
public INodeData Clone();
17+
public int Length { get; }
18+
public ref object this[int index] { get; }
19+
public int MemorySize { get; }
20+
}
21+
22+
interface INodeWithKey : INodeData
23+
{
24+
public byte[] Key { get; set; }
25+
}
26+
27+
public class BranchData : INodeData
28+
{
29+
public NodeType NodeType => NodeType.Branch;
30+
public int Length => TrieNode.BranchesCount;
31+
public int MemorySize => MemorySizes.RefSize * TrieNode.BranchesCount;
32+
33+
private BranchArray _branches;
34+
35+
public BranchData() { }
36+
37+
private BranchData(in BranchArray branches) => _branches = branches;
38+
39+
public ref readonly BranchArray Branches => ref _branches;
40+
public ref object this[int index] => ref _branches[index];
41+
42+
INodeData INodeData.Clone() => new BranchData(in _branches);
43+
44+
[InlineArray(Length)]
45+
public struct BranchArray
46+
{
47+
public const int Length = TrieNode.BranchesCount;
48+
private object? _element0;
49+
}
50+
}
51+
52+
public class ExtensionData : INodeWithKey
53+
{
54+
public NodeType NodeType => NodeType.Extension;
55+
public int MemorySize => MemorySizes.RefSize + MemorySizes.RefSize +
56+
(_key is not null ? (int)MemorySizes.Align(_key.Length + MemorySizes.ArrayOverhead) : 0);
57+
public int Length => 2;
58+
59+
public byte[]? _key;
60+
public object? _value;
61+
public byte[] Key { get => _key; set => _key = value; }
62+
public object? Value { get => _value; set => _value = value; }
63+
public ref object this[int index]
64+
{
65+
get
66+
{
67+
if ((uint)index >= (uint)Length)
68+
{
69+
ThrowArgumentOutOfRangeException(index);
70+
}
71+
72+
return ref _value;
73+
74+
[DoesNotReturn]
75+
[StackTraceHidden]
76+
static void ThrowArgumentOutOfRangeException(int index)
77+
{
78+
throw new ArgumentOutOfRangeException(nameof(index), index, $"{index} is not 0 or 1");
79+
}
80+
}
81+
}
82+
83+
public ExtensionData() { }
84+
85+
internal ExtensionData(byte[] key)
86+
{
87+
Key = key;
88+
}
89+
90+
internal ExtensionData(byte[] key, TrieNode value)
91+
{
92+
Key = key;
93+
Value = value;
94+
}
95+
96+
private ExtensionData(byte[] key, object? value)
97+
{
98+
Key = key;
99+
Value = value;
100+
}
101+
102+
INodeData INodeData.Clone() => new ExtensionData(Key, Value);
103+
}
104+
105+
public class LeafData : INodeWithKey
106+
{
107+
public NodeType NodeType => NodeType.Leaf;
108+
public int Length => 0;
109+
public int MemorySize => MemorySizes.RefSize + MemorySizes.RefSize + MemorySizes.RefSize +
110+
(Key is not null ? (int)MemorySizes.Align(Key.Length + MemorySizes.ArrayOverhead) : 0) +
111+
(_value.IsNotNull ? (int)MemorySizes.Align(_value.Length + MemorySizes.ArrayOverhead) : 0);
112+
113+
private readonly CappedArray<byte> _value;
114+
115+
public byte[] Key { get; set; }
116+
public ref readonly CappedArray<byte> Value => ref _value;
117+
public TrieNode? StorageRoot { get; set; }
118+
119+
public LeafData() { }
120+
121+
internal LeafData(byte[] key, in CappedArray<byte> value)
122+
{
123+
Key = key;
124+
_value = value;
125+
}
126+
127+
private LeafData(byte[] key, in CappedArray<byte> value, TrieNode? storageRoot)
128+
{
129+
Key = key;
130+
_value = value;
131+
StorageRoot = storageRoot;
132+
}
133+
public ref object this[int index] => throw new IndexOutOfRangeException();
134+
135+
INodeData INodeData.Clone() => new LeafData(Key, in _value);
136+
public LeafData CloneWithNewValue(in CappedArray<byte> value) => new LeafData(Key, in value, StorageRoot);
137+
}

0 commit comments

Comments
 (0)