Skip to content
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

Hunting allocations 3 - introduce AccountStruct #6605

Merged
merged 20 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldS

foreach (KeyValuePair<UInt256, byte[]> clearedStorage in clearedStorages)
{
byte[] value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, clearedStorage.Key));
Span<byte> value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, clearedStorage.Key));
if (!value.IsZero())
{
differences.Add($"{acountAddress} storage[{clearedStorage.Key}] exp: 0x00, actual: {value.ToHexString(true)}");
Expand All @@ -415,7 +415,7 @@ private List<string> RunAssertions(BlockchainTest test, Block headBlock, IWorldS

foreach (KeyValuePair<UInt256, byte[]> storageItem in accountState.Storage)
{
byte[] value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, storageItem.Key)) ?? new byte[0];
Span<byte> value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, storageItem.Key));
if (!Bytes.AreEqual(storageItem.Value, value))
{
differences.Add($"{acountAddress} storage[{storageItem.Key}] exp: {storageItem.Value.ToHexString(true)}, actual: {value.ToHexString(true)}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public async Task Should_execute_well_formed_op_successfully_if_codehash_not_cha
chain.SendUserOperation(entryPointAddress[0], op);
if (changeCodeHash)
{
chain.State.InsertCode(walletAddress[0]!, Bytes.Concat(chain.State.GetCode(walletAddress[0]!), 0x00), chain.SpecProvider.GenesisSpec);
chain.State.InsertCode(walletAddress[0]!, Bytes.Concat(chain.State.GetCode(walletAddress[0]!)!, 0x00), chain.SpecProvider.GenesisSpec);
chain.State.Commit(chain.SpecProvider.GenesisSpec);
chain.State.RecalculateStateRoot();
chain.State.CommitTree(chain.BlockTree.Head!.Number);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ public void Should_make_sure_external_contract_extcodehashes_stays_same_after_si
.Op(Instruction.STOP)
.Done;

Hash256 initialCodeHash = TestState.GetCodeHash(externalContractAddress);
(UserOperationTxTracer tracer, _, _) = ExecuteAndTraceAccessCall(SenderRecipientAndMiner.Default, code, whitelisted);
ValueHash256 initialCodeHash = TestState.GetCodeHash(externalContractAddress);
ExecuteAndTraceAccessCall(SenderRecipientAndMiner.Default, code, whitelisted);
if (shouldMatch)
{
TestState.GetCodeHash(externalContractAddress).Should().Be(initialCodeHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public ResultWrapper<Hash256> Simulate(UserOperation userOperation,
foreach (KeyValuePair<Address, Hash256> kv in userOperation.AddressesToCodeHashes)
{
(Address address, Hash256 expectedCodeHash) = kv;
Hash256 codeHash = _stateProvider.GetCodeHash(address);
ValueHash256 codeHash = _stateProvider.GetCodeHash(address);
if (codeHash != expectedCodeHash)
{
return ResultWrapper<Hash256>.Fail($"codehash of address {address} changed since initial simulation");
Expand Down Expand Up @@ -148,7 +148,7 @@ private UserOperationSimulationResult SimulateValidation(
IDictionary<Address, Hash256> addressesToCodeHashes = new Dictionary<Address, Hash256>();
foreach (Address accessedAddress in txTracer.AccessedAddresses)
{
addressesToCodeHashes[accessedAddress] = _stateProvider.GetCodeHash(accessedAddress);
addressesToCodeHashes[accessedAddress] = new Hash256(_stateProvider.GetCodeHash(accessedAddress));
}

return new UserOperationSimulationResult()
Expand Down
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Analytics/SupplyVerifier.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using Nethermind.Core;
using Nethermind.Core.Crypto;
Expand Down Expand Up @@ -78,7 +79,7 @@ public void VisitExtension(TrieNode node, TrieVisitContext trieVisitContext)
}
}

public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, byte[] value = null)
public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, ReadOnlySpan<byte> value)
{
_nodesVisited++;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void transactions_are_addable_to_block_after_sealing()
Address nodeAddress = TestItem.AddressA;

UInt256 expectedNonce = 10;
stateReader.GetAccount(blockHeader.StateRoot, nodeAddress).Returns(Account.TotallyEmpty.WithChangedNonce(expectedNonce));
stateReader.GetAccount(blockHeader.StateRoot, nodeAddress).Returns(new AccountStruct(expectedNonce, UInt256.Zero));

ulong expectedTimeStamp = 100;
timestamper.UnixTime.Returns(UnixTime.FromSeconds(expectedTimeStamp));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void VisitMissingNode(Hash256 nodeHash, TrieVisitContext trieVisitContext

public void VisitExtension(TrieNode node, TrieVisitContext trieVisitContext) => PersistNode(node);

public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, byte[]? value = null) => PersistNode(node);
public void VisitLeaf(TrieNode node, TrieVisitContext trieVisitContext, ReadOnlySpan<byte> value) => PersistNode(node);

public void VisitCode(Hash256 codeHash, TrieVisitContext trieVisitContext) { }

Expand All @@ -78,7 +78,7 @@ private void PersistNode(TrieNode node)
if (node.Keccak is not null)
{
// simple copy of nodes RLP
_pruningContext.Set(node.Keccak.Bytes, node.FullRlp.ToArray(), _writeFlags);
_pruningContext.PutSpan(node.Keccak.Bytes, node.FullRlp.AsSpan(), _writeFlags);
Interlocked.Increment(ref _persistedNodes);

// log message every 1 mln nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ref struct KeccaksIterator
private readonly Span<byte> _buffer;
public long Index { get; private set; }

public KeccaksIterator(Span<byte> data, Span<byte> buffer)
public KeccaksIterator(ReadOnlySpan<byte> data, Span<byte> buffer)
{
if (buffer.Length != 32) throw new ArgumentException("Buffer must be 32 bytes long");
_decoderContext = new Rlp.ValueDecoderContext(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ref struct LogEntriesIterator
private readonly IReceiptRefDecoder _receiptRefDecoder;
public long Index { get; private set; }

public LogEntriesIterator(Span<byte> data, IReceiptRefDecoder receiptRefDecoder)
public LogEntriesIterator(ReadOnlySpan<byte> data, IReceiptRefDecoder receiptRefDecoder)
{
_decoderContext = new Rlp.ValueDecoderContext(data);
_length = _decoderContext.ReadSequenceLength();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,26 @@ public SpecificBlockReadOnlyStateProvider(IStateReader stateReader, Hash256? sta

public virtual Hash256 StateRoot { get; }

public Account GetAccount(Address address) => _stateReader.GetAccount(StateRoot, address) ?? Account.TotallyEmpty;
public AccountStruct GetAccount(Address address) => _stateReader.GetAccount(StateRoot, address) ?? default;

public bool IsContract(Address address) => GetAccount(address).IsContract;

public UInt256 GetNonce(Address address) => GetAccount(address).Nonce;

public UInt256 GetBalance(Address address) => GetAccount(address).Balance;

public Hash256? GetStorageRoot(Address address) => GetAccount(address).StorageRoot;
public ValueHash256 GetStorageRoot(Address address) => GetAccount(address).StorageRoot;

public byte[] GetCode(Address address)
public byte[]? GetCode(Address address)
{
Account account = GetAccount(address);
if (!account.HasCode)
{
return Array.Empty<byte>();
}

return _stateReader.GetCode(account.CodeHash);
AccountStruct account = GetAccount(address);
return !account.HasCode ? Array.Empty<byte>() : _stateReader.GetCode(account.CodeHash);
}

public byte[] GetCode(Hash256 codeHash) => _stateReader.GetCode(codeHash);
public byte[]? GetCode(Hash256 codeHash) => _stateReader.GetCode(codeHash);
public byte[]? GetCode(ValueHash256 codeHash) => _stateReader.GetCode(codeHash);

public Hash256 GetCodeHash(Address address)
{
Account account = GetAccount(address);
return account.CodeHash;
}
public ValueHash256 GetCodeHash(Address address) => GetAccount(address).CodeHash;

public void Accept(ITreeVisitor visitor, Hash256 stateRoot, VisitingOptions? visitingOptions)
{
Expand All @@ -59,15 +51,8 @@ public void Accept(ITreeVisitor visitor, Hash256 stateRoot, VisitingOptions? vis
public bool AccountExists(Address address) => _stateReader.GetAccount(StateRoot, address) is not null;

public bool IsEmptyAccount(Address address) => GetAccount(address).IsEmpty;
public bool HasStateForRoot(Hash256 stateRoot)
{
return _stateReader.HasStateForRoot(stateRoot);
}
public bool HasStateForRoot(Hash256 stateRoot) => _stateReader.HasStateForRoot(stateRoot);

public bool IsDeadAccount(Address address)
{
Account account = GetAccount(address);
return account.IsEmpty;
}
public bool IsDeadAccount(Address address) => GetAccount(address).IsEmpty;
}
}
41 changes: 40 additions & 1 deletion src/Nethermind/Nethermind.Core/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Nethermind.Core
{
public class Account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah still exist?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are places where we need to put it on heap, not sure if worth paying for boxing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But will try to move some usages to AccountStruct in next PR.

{
public static Account TotallyEmpty = new();
public static readonly Account TotallyEmpty = new();

private readonly Hash256? _codeHash;
private readonly Hash256? _storageRoot;
Expand Down Expand Up @@ -100,5 +100,44 @@ public Account WithChangedCodeHash(Hash256 newCodeHash)
{
return new(newCodeHash, this);
}

public AccountStruct ToStruct() => new(Nonce, Balance, StorageRoot, CodeHash);
}

public struct AccountStruct
LukaszRozmej marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly ValueHash256 _codeHash = Keccak.OfAnEmptyString.ValueHash256;
private readonly ValueHash256 _storageRoot = Keccak.EmptyTreeHash.ValueHash256;

public AccountStruct(in UInt256 nonce, in UInt256 balance, in ValueHash256 storageRoot, in ValueHash256 codeHash)
{
_codeHash = codeHash;
_storageRoot = storageRoot;
Nonce = nonce;
Balance = balance;
}

public AccountStruct(in UInt256 nonce, in UInt256 balance)
{
Nonce = nonce;
Balance = balance;
}

public AccountStruct(in UInt256 balance)
{
Balance = balance;
}

public bool HasCode => _codeHash != Keccak.OfAnEmptyString.ValueHash256;

public bool HasStorage => _storageRoot != Keccak.EmptyTreeHash.ValueHash256;

public UInt256 Nonce { get; }
public UInt256 Balance { get; }
public ValueHash256 StorageRoot => _storageRoot;
public ValueHash256 CodeHash => _codeHash;
public bool IsTotallyEmpty => _storageRoot == Keccak.EmptyTreeHash.ValueHash256 && IsEmpty;
public bool IsEmpty => _codeHash == Keccak.OfAnEmptyString.ValueHash256 && Balance.IsZero && Nonce.IsZero;
public bool IsContract => _codeHash != Keccak.OfAnEmptyString.ValueHash256;
}
}
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.Core/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public ref struct AddressStructRef
private const int HexCharsCount = 2 * ByteLength; // 5a4eab120fb44eb6684e5e32785702ff45ea344d
private const int PrefixedHexCharsCount = 2 + HexCharsCount; // 0x5a4eab120fb44eb6684e5e32785702ff45ea344d

public Span<byte> Bytes { get; }
public ReadOnlySpan<byte> Bytes { get; }

public AddressStructRef(Hash256StructRef keccak) : this(keccak.Bytes.Slice(12, ByteLength)) { }

Expand Down Expand Up @@ -274,7 +274,7 @@ public static bool IsValidAddress(string hexString, bool allowPrefix)

public AddressStructRef(string hexString) : this(Extensions.Bytes.FromHexString(hexString)) { }

public AddressStructRef(Span<byte> bytes)
public AddressStructRef(ReadOnlySpan<byte> bytes)
{
if (bytes.Length != ByteLength)
{
Expand Down
56 changes: 2 additions & 54 deletions src/Nethermind/Nethermind.Core/Bloom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,37 +229,12 @@ public ref struct BloomStructRef
public const int BitLength = 2048;
public const int ByteLength = BitLength / 8;

public BloomStructRef(LogEntry[] logEntries, Bloom? blockBloom = null)
{
Bytes = new byte[ByteLength];
Add(logEntries, blockBloom);
}

public BloomStructRef(Span<byte> bytes)
public BloomStructRef(ReadOnlySpan<byte> bytes)
{
Bytes = bytes;
}

public Span<byte> Bytes { get; }

public void Set(ReadOnlySpan<byte> sequence)
{
Set(sequence, null);
}

private readonly void Set(ReadOnlySpan<byte> sequence, Bloom? masterBloom = null)
{
Bloom.BloomExtract indexes = GetExtract(sequence);
Set(indexes.Index1);
Set(indexes.Index2);
Set(indexes.Index3);
if (masterBloom is not null)
{
masterBloom.Set(indexes.Index1);
masterBloom.Set(indexes.Index2);
masterBloom.Set(indexes.Index3);
}
}
public ReadOnlySpan<byte> Bytes { get; }

public bool Matches(ReadOnlySpan<byte> sequence)
{
Expand Down Expand Up @@ -306,26 +281,6 @@ public override readonly int GetHashCode()
return Core.Extensions.Bytes.GetSimplifiedHashCode(Bytes);
}

public void Add(LogEntry[] logEntries, Bloom? blockBloom)
{
for (int entryIndex = 0; entryIndex < logEntries.Length; entryIndex++)
{
LogEntry logEntry = logEntries[entryIndex];
byte[] addressBytes = logEntry.LoggersAddress.Bytes;
Set(addressBytes, blockBloom);
for (int topicIndex = 0; topicIndex < logEntry.Topics.Length; topicIndex++)
{
Hash256 topic = logEntry.Topics[topicIndex];
Set(topic.Bytes, blockBloom);
}
}
}

public readonly void Accumulate(BloomStructRef bloom)
{
Bytes.Or(bloom.Bytes);
}

public bool Matches(LogEntry logEntry)
{
if (Matches(logEntry.LoggersAddress))
Expand All @@ -351,13 +306,6 @@ private readonly bool Get(int index)
return Bytes[bytePosition].GetBit(shift);
}

private readonly void Set(int index)
{
int bytePosition = index / 8;
int shift = index % 8;
Bytes[bytePosition].SetBit(shift);
}

public bool Matches(Address address) => Matches(address.Bytes);

public bool Matches(Hash256 topic) => Matches(topic.Bytes);
Expand Down
23 changes: 23 additions & 0 deletions src/Nethermind/Nethermind.Core/ByteReadOnlyMemoryConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Nethermind.Serialization.Json;

public class ByteReadOnlyMemoryConverter : JsonConverter<ReadOnlyMemory<byte>>
{
public override ReadOnlyMemory<byte> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
ByteArrayConverter.Convert(ref reader);

public override void Write(
Utf8JsonWriter writer,
ReadOnlyMemory<byte> bytes,
JsonSerializerOptions options) =>
ByteArrayConverter.Convert(writer, bytes.Span, skipLeadingZeros: false);
}
5 changes: 3 additions & 2 deletions src/Nethermind/Nethermind.Core/Crypto/Hash256.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Nethermind.Core.Crypto
{
[DebuggerStepThrough]
[DebuggerDisplay("{ToString()}")]
[JsonConverter(typeof(ValueHash256Converter))]
public readonly struct ValueHash256 : IEquatable<ValueHash256>, IComparable<ValueHash256>, IEquatable<Hash256>
{
private readonly Vector256<byte> _bytes;
Expand Down Expand Up @@ -282,9 +283,9 @@ public ref struct Hash256StructRef

public static int MemorySize => MemorySizes.ArrayOverhead + Size;

public Span<byte> Bytes { get; }
public ReadOnlySpan<byte> Bytes { get; }

public Hash256StructRef(Span<byte> bytes)
public Hash256StructRef(ReadOnlySpan<byte> bytes)
{
if (bytes.Length != Size)
{
Expand Down
Loading