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

Tree reporting #102

Merged
merged 4 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 39 additions & 4 deletions src/Paprika.Runner/Program.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using HdrHistogram;
using Nethermind.Int256;
using Paprika.Chain;
using Paprika.Crypto;
using Paprika.Store;
using Paprika.Tests;
using Spectre.Console;
using Spectre.Console.Rendering;

[assembly: ExcludeFromCodeCoverage]

namespace Paprika.Runner;

public static class Program
{
private const int BlockCount = PersistentDb ? 25_000 : 3_000;
private const int RandomSampleSize = 260_000_000;
private const int BlockCount = PersistentDb ? 20_000 : 3_000;
private const int AccountsPerBlock = 1000;
private const int MaxReorgDepth = 64;
private const int FinalizeEvery = 32;
Expand All @@ -27,7 +28,7 @@ public static class Program
private const long DbFileSize = PersistentDb ? 256 * Gb : 16 * Gb;
private const long Gb = 1024 * 1024 * 1024L;

private static readonly TimeSpan FlushEvery = TimeSpan.FromSeconds(5);
private static readonly TimeSpan FlushEvery = TimeSpan.FromSeconds(30);

private const int LogEvery = BlockCount / NumberOfLogs;

Expand Down Expand Up @@ -131,7 +132,7 @@ public static async Task Main(String[] args)
}

// waiting for finalization
var read = db.BeginReadOnlyBatch();
using var read = db.BeginReadOnlyBatch();

var readingStopWatch = Stopwatch.StartNew();
random = BuildRandom();
Expand Down Expand Up @@ -184,6 +185,29 @@ public static async Task Main(String[] args)
// the final report
ReportReading(counter);

var stats = new StatisticsReporter();
read.Report(stats);
var table = new Table();

table.AddColumn(new TableColumn("Level of Paprika tree"));
table.AddColumn(new TableColumn("Child page count"));
table.AddColumn(new TableColumn("Entries in page"));

foreach (var (key, level) in stats.Levels)
{
table.AddRow(
new Text(key.ToString()),
WriteHistogram(level.ChildCount),
WriteHistogram(level.Entries));
}

var mb = (long)stats.PageCount * Page.PageSize / 1024 / 1024;
var report = new Layout().SplitRows(
new Layout(new Paragraph($"General stats:\n1. Size of this Paprika tree: {mb}MB")).Size(3),
new Layout(table.Expand()));

layout[info].Update(new Panel(report).Header("Paprika tree statistics").Expand());

spectre.Cancel();
await reportingTask;

Expand Down Expand Up @@ -220,6 +244,17 @@ void ReportReading(int i)

private static Random BuildRandom() => new(RandomSeed);

private static IRenderable WriteHistogram(HistogramBase histogram)
{
string Percentile(int percentile, string color)
{
var value = histogram.GetValueAtPercentile(percentile);
return $"[{color}]P{percentile}: {value,2}[/] ";
}

return new Markup(Percentile(50, "green") + Percentile(90, "yellow") + Percentile(95, "red"));
}

private static int Writer(Blockchain blockchain, Keccak bigStorageAccount, Random random,
Layout reporting)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Paprika/Chain/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ public bool TryGet(in Key key, out ReadOnlySpan<byte> result)
Dispose();
}
}

public void Report(IReporter reporter) =>
throw new NotImplementedException("One should not report over a block");
}

/// <summary>
Expand Down
9 changes: 9 additions & 0 deletions src/Paprika/Data/HashingMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ public bool TryGet(uint hash, in Key key, out ReadOnlySpan<byte> value)
return false;
}

public int Count
{
get
{
var indexOf = _hashes.IndexOf(NoHash);
return indexOf == IndexOfNotFound ? _hashes.Length : indexOf;
}
}

private Span<byte> GetEntry(int position) => _entries.Slice(position * EntrySize, EntrySize);

public bool TrySet(uint hash, in Key key, ReadOnlySpan<byte> value)
Expand Down
6 changes: 3 additions & 3 deletions src/Paprika/IReadOnlyBatch.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Nethermind.Int256;
using Paprika.Crypto;
using Paprika.Data;
using Paprika.Data;
using Paprika.Store;

namespace Paprika;
Expand All @@ -16,4 +14,6 @@ public interface IReadOnlyBatch : IDisposable
/// <param name="result"></param>
/// <returns></returns>
bool TryGet(in Key key, out ReadOnlySpan<byte> result);

void Report(IReporter reporter);
}
1 change: 1 addition & 0 deletions src/Paprika/Paprika.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HdrHistogram" Version="2.5.0" />
<PackageReference Include="Nethermind.Numerics.Int256" Version="1.1.0" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0-preview.4.23259.5" />
</ItemGroup>
Expand Down
30 changes: 29 additions & 1 deletion src/Paprika/Store/DataPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public struct Payload
{
private const int Size = Page.PageSize - PageHeader.Size;

private const int BucketCount = 16;
public const int BucketCount = 16;

/// <summary>
/// The size of the raw byte data held in this page. Must be long aligned.
Expand Down Expand Up @@ -248,6 +248,34 @@ public bool TryGet(uint hash, Key key, IReadOnlyBatchContext batch, out ReadOnly
return false;
}

public void Report(IReporter reporter, IPageResolver resolver, int level)
{
var emptyBuckets = 0;

foreach (var bucket in Data.Buckets)
{
if (bucket.IsNull)
{
emptyBuckets++;
}
else
{
new DataPage(resolver.GetAt(bucket)).Report(reporter, resolver, level + 1);
}
}

if (emptyBuckets == 0)
{
// all filled
reporter.Report(level, Payload.BucketCount, new HashingMap(Data.DataSpan).Count);
}
else
{
reporter.Report(level, Payload.BucketCount - emptyBuckets,
new NibbleBasedMap(Data.DataSpan).Count);
}
}

private static bool TryFindExistingStorageTreeForCellOf(in NibbleBasedMap map, in Key key,
out DbAddress storageTreeAddress)
{
Expand Down
36 changes: 36 additions & 0 deletions src/Paprika/Store/IReporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using HdrHistogram;

namespace Paprika.Store;

/// <summary>
/// Provides capability to report stats for <see cref="DataPage"/>.
/// </summary>
public interface IReporter
{
void Report(int level, int filledBuckets, int entriesPerPage);
}

public class StatisticsReporter : IReporter
{
public readonly SortedDictionary<int, Level> Levels = new();
public int PageCount = 0;

public void Report(int level, int filledBuckets, int entriesPerPage)
{
if (Levels.TryGetValue(level, out var lvl) == false)
{
lvl = Levels[level] = new Level();
}

PageCount++;

lvl.ChildCount.RecordValue(filledBuckets);
lvl.Entries.RecordValue(entriesPerPage);
}

public class Level
{
public readonly IntHistogram ChildCount = new(1000, 5);
public readonly IntHistogram Entries = new(1000, 5);
}
}
2 changes: 1 addition & 1 deletion src/Paprika/Store/Page.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public enum PageType : byte
/// </summary>
public readonly unsafe struct Page : IPage, IEquatable<Page>
{
public const int PageCount = 0x0100_0000; // 64GB addressable
public const int PageCount = 0x1000_0000; // 64GB addressable
public const int PageAddressMask = PageCount - 1;
public const int PageSize = 4 * 1024;

Expand Down
22 changes: 22 additions & 0 deletions src/Paprika/Store/PagedDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@ public bool TryGet(in Key key, out ReadOnlySpan<byte> result)
return new DataPage(GetAt(addr)).TryGet(hash, sliced, this, out result);
}

public void Report(IReporter reporter)
{
foreach (var addr in _rootDataPages)
{
if (addr.IsNull == false)
{
new DataPage(GetAt(addr)).Report(reporter, this, 1);
}
}
}

public uint BatchId { get; }

public Page GetAt(DbAddress address) => _db._manager.GetAt(address);
Expand Down Expand Up @@ -388,6 +399,17 @@ private void CheckDisposed()
}
}

public void Report(IReporter reporter)
{
foreach (var addr in _root.Data.AccountPages)
{
if (addr.IsNull == false)
{
new DataPage(GetAt(addr)).Report(reporter, this, 1);
}
}
}

public async ValueTask Commit(CommitOptions options)
{
var watch = Stopwatch.StartNew();
Expand Down