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

Added StoreCache #3701

Closed
108 changes: 108 additions & 0 deletions benchmarks/Neo.Benchmarks/Collections/Caching/Benchmarks.StoreCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// Benchmarks.StoreCache.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Order;
using Neo.Collections.Caching;
using Neo.Persistence;
using Neo.SmartContract;
using Perfolizer.Mathematics.OutlierDetection;
using System.Diagnostics;

namespace Neo.Benchmark.Collections.Caching
{
// Result Exporters
[MarkdownExporter] // Exporting results in Markdown format
// Result Output
[MinColumn, MaxColumn, MeanColumn, MedianColumn] // Include these columns
[Orderer(SummaryOrderPolicy.Declared, MethodOrderPolicy.Declared)] // Keep in current order as declared in class
// Job Configurations
[SimpleJob(RuntimeMoniker.Net90)]
[Outliers(OutlierMode.DontRemove)]
[GcServer(true)] // GC server is enabled for GitHub builds in `neo` repo
[GcConcurrent(true)] // GC runs on it own thread
[GcForce(false)] // DO NOT force full collection of data for each benchmark
public class Benchmarks_StoreCache
Copy link
Member

Choose a reason for hiding this comment

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

Why the benchmarks are not the same but changing the interface? it looks like the methods are totally different

Copy link
Member Author

Choose a reason for hiding this comment

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

Methods do the same thing. However, StoreCache class is a collection, so you can convert StoreCache to any collection you want. This is how DataCache and other classes in neo should of been developed.

StoreCache inherits from

ICollection<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>
IEnumerable
IDictionary<TKey, TValue>
IReadOnlyCollection<KeyValuePair<TKey, TValue>>
IReadOnlyDictionary<TKey, TValue>
ICollection
IDictionary

{
private readonly MemoryStore _memoryStore = new();
private readonly StoreCache<StorageKey, StorageItem> _storeCache;
private readonly DataCache _dataCache;

private readonly StorageKey _key;
private readonly StorageItem _value;

public Benchmarks_StoreCache()
{
_storeCache = new(_memoryStore);
_dataCache = new SnapshotCache(_memoryStore);

var data = new byte[1024];
new Random(0xdead).NextBytes(data);

_memoryStore.Put(data, data);
_key = new(data);
_value = new(data);
}

[Benchmark]
public void TestStoreCacheAdd()
{
_storeCache.Add(_key, _value);
}

[Benchmark]
public void TestDataCacheAdd()
{
Debug.Assert(_dataCache.GetOrAdd(_key, () => _value) != null);
}

[Benchmark]
public void TestStoreCacheUpdate()
{
Debug.Assert(_storeCache.Update(_key, _value));
}

[Benchmark]
public void TestDataCacheUpdate()
{
Debug.Assert(_dataCache.GetAndChange(_key, () => _value) != null);
}

[Benchmark]
public void TestStoreCacheRemove()
{
_storeCache.Remove(_key);
}

[Benchmark]
public void TestDataCacheRemove()
{
_dataCache.Delete(_key);
}

[Benchmark]
public void TestStoreCacheGet()
{
Debug.Assert(_storeCache.TryGetValue(_key, out _));
Debug.Assert(_storeCache.ContainsKey(_key));
Debug.Assert(_storeCache[_key] != null);
}

[Benchmark]
public void TestDataCacheGet()
{
Debug.Assert(_dataCache.GetAndChange(_key) != null);
Debug.Assert(_dataCache.Contains(_key));
Debug.Assert(_dataCache[_key] != null);
}
}
}
104 changes: 104 additions & 0 deletions benchmarks/Neo.Benchmarks/IO/Caching/Benchmarks.StorageCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// Benchmarks.StorageCache.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Order;
using Neo.IO.Caching;
using Neo.Persistence;
using Neo.SmartContract;
using Perfolizer.Mathematics.OutlierDetection;
using System.Diagnostics;

namespace Neo.Benchmark.IO.Caching
{
// Result Exporters
[MarkdownExporter] // Exporting results in Markdown format
// Result Output
[MinColumn, MaxColumn, MeanColumn, MedianColumn] // Include these columns
[Orderer(SummaryOrderPolicy.Declared, MethodOrderPolicy.Declared)] // Keep in current order as declared in class
// Job Configurations
[SimpleJob(RuntimeMoniker.Net90)]
[Outliers(OutlierMode.DontRemove)]
[GcServer(true)] // GC server is enabled for GitHub builds in `neo` repo
[GcConcurrent(true)] // GC runs on it own thread
[GcForce(false)] // DO NOT force full collection of data for each benchmark
public class Benchmarks_StorageCache
{
private readonly MemoryStore _memoryStore = new();
private readonly StorageCache<StorageKey, StorageItem> _storageCache;
private readonly DataCache _dataCache;

private readonly StorageKey _key;
private readonly StorageItem _value;

public Benchmarks_StorageCache()
{
_storageCache = new(_memoryStore);
_dataCache = new SnapshotCache(_memoryStore);

var data = new byte[1024];
new Random(0xdead).NextBytes(data);

_memoryStore.Put(data, data);
_key = new(data);
_value = new(data);
}

[Benchmark]
public void TestStoreCacheAdd()
{
_storageCache.AddOrUpdate(_key, _value);
}

[Benchmark]
public void TestDataCacheAdd()
{
Debug.Assert(_dataCache.GetOrAdd(_key, () => _value) != null);
}

[Benchmark]
public void TestStoreCacheUpdate()
{
_storageCache.AddOrUpdate(_key, _value);
}

[Benchmark]
public void TestDataCacheUpdate()
{
Debug.Assert(_dataCache.GetAndChange(_key, () => _value) != null);
}

[Benchmark]
public void TestStoreCacheDelete()
{
_storageCache.Delete(_key);
}

[Benchmark]
public void TestDataCacheDelete()
{
_dataCache.Delete(_key);
}

[Benchmark]
public void TestStoreCacheGet()
{
Debug.Assert(_storageCache.TryGetValue(_key, out _));
}

[Benchmark]
public void TestDataCacheGet()
{
Debug.Assert(_dataCache.GetAndChange(_key) != null);
}
}
}
5 changes: 4 additions & 1 deletion benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@

<ItemGroup>
<PackageReference Include="System.IO.Hashing" Version="9.0.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Neo\Neo.csproj" />
<ProjectReference Include="..\..\src\Plugins\LevelDBStore\LevelDBStore.csproj" />
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
</ItemGroup>

<ItemGroup>
Expand Down
15 changes: 7 additions & 8 deletions benchmarks/Neo.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
// modifications are permitted.

using BenchmarkDotNet.Running;
using Neo.Benchmark;
using Neo.Benchmarks.Persistence.Benchmarks;
using Neo.SmartContract.Benchmark;
using Neo.Benchmark.IO.Caching;

// BenchmarkRunner.Run<Benchmarks_PoCs>();
BenchmarkRunner.Run<Benchmarks_UInt160>();
BenchmarkRunner.Run<Benchmarks_Hash>();
BenchmarkRunner.Run<Benchmarks_StorageKey>();
BenchmarkRunner.Run<Bechmarks_ReadOnlyStoreView>();
BenchmarkRunner.Run<Bechmarks_LevelDB>();
//BenchmarkRunner.Run<Benchmarks_UInt160>();
//BenchmarkRunner.Run<Benchmarks_Hash>();
//BenchmarkRunner.Run<Benchmarks_StorageKey>();
//BenchmarkRunner.Run<Bechmarks_ReadOnlyStoreView>();
//BenchmarkRunner.Run<Bechmarks_LevelDB>();
BenchmarkRunner.Run<Benchmarks_StorageCache>();
22 changes: 13 additions & 9 deletions src/Neo.Extensions/ByteArrayComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@
// modifications are permitted.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace Neo.Extensions
{
public class ByteArrayComparer : IComparer<byte[]>
public class ByteArrayComparer : IComparer<byte[]>, IComparer
{
public static readonly ByteArrayComparer Default = new(1);
public static readonly ByteArrayComparer Reverse = new(-1);

private readonly int _direction;
private readonly sbyte _direction;

private ByteArrayComparer(int direction)
{
_direction = direction;
_direction = unchecked((sbyte)direction);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(byte[]? x, byte[]? y)
{
if (x == y) return 0;
if (ReferenceEquals(x, y)) return 0;

if (x is null) // y must not be null
return -y!.Length * _direction;

if (y is null) // x must not be null
return x.Length * _direction;
x ??= [];
y ??= [];

if (_direction < 0)
return y.AsSpan().SequenceCompareTo(x.AsSpan());
return x.AsSpan().SequenceCompareTo(y.AsSpan());
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Compare(object? x, object? y)
{
return Compare(x as byte[], y as byte[]);
}
}
}
21 changes: 15 additions & 6 deletions src/Neo.Extensions/ByteArrayEqualityComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,35 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Neo.Extensions
{
public class ByteArrayEqualityComparer : IEqualityComparer<byte[]>
public class ByteArrayEqualityComparer : IEqualityComparer<byte[]>, IEqualityComparer
{
public static readonly ByteArrayEqualityComparer Default = new();

public bool Equals(byte[]? x, byte[]? y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null || y is null || x.Length != y.Length) return false;
if (x is null || y is null) return false;
if (x.Length != y.Length) return false;

return x.AsSpan().SequenceEqual(y.AsSpan());
return GetHashCode(x) == GetHashCode(y);
}

public int GetHashCode(byte[] obj)
public new bool Equals(object? x, object? y)
{
return obj.XxHash3_32();
if (ReferenceEquals(x, y)) return true;
return Equals(x as byte[], y as byte[]);
}

public int GetHashCode([DisallowNull] byte[] obj) =>
obj.XxHash3_32();

public int GetHashCode([DisallowNull] object obj) =>
obj is byte[] b ? GetHashCode(b) : 0;
}
}
18 changes: 18 additions & 0 deletions src/Neo.IO/IKeySerializable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2015-2025 The Neo Project.
//
// IKeySerializable.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.IO
{
public interface IKeySerializable
{
byte[] ToArray();
}
}
Loading
Loading