Skip to content

Commit 543fed8

Browse files
committed
add benchmark
1 parent 654c926 commit 543fed8

File tree

4 files changed

+409
-0
lines changed

4 files changed

+409
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// Copyright (C) 2015-2025 The Neo Project.
2+
//
3+
// Benchmarks.MemoryPool.cs file belongs to the neo project and is free
4+
// software distributed under the MIT software license, see the
5+
// accompanying file LICENSE in the main directory of the
6+
// repository or http://www.opensource.org/licenses/mit-license.php
7+
// for more details.
8+
//
9+
// Redistribution and use in source and binary forms with or without
10+
// modifications are permitted.
11+
12+
using BenchmarkDotNet.Attributes;
13+
using Moq;
14+
using Neo.Ledger;
15+
using Neo.Network.P2P.Payloads;
16+
using Neo.Persistence;
17+
using Neo.Persistence.Providers;
18+
using Neo.SmartContract;
19+
using Neo.VM;
20+
using System;
21+
using System.Collections.Generic;
22+
using System.Linq;
23+
using System.Reflection;
24+
25+
namespace Neo.Benchmark
26+
{
27+
/// <summary>
28+
/// A specialized MemoryPool for benchmarking that exposes the internal TryAdd method
29+
/// </summary>
30+
public class BenchmarkMemoryPool : MemoryPool
31+
{
32+
public BenchmarkMemoryPool() : base(MockNeoSystem())
33+
{
34+
}
35+
36+
private static NeoSystem MockNeoSystem()
37+
{
38+
// Use reflection to create a minimal NeoSystem without initializing all components
39+
var constructor = typeof(NeoSystem).GetConstructor(
40+
BindingFlags.NonPublic | BindingFlags.Instance,
41+
null, new Type[] { typeof(ProtocolSettings) }, null);
42+
43+
var settings = ProtocolSettings.Default with { MemoryPoolMaxTransactions = 1000 };
44+
return (NeoSystem)constructor.Invoke(new object[] { settings });
45+
}
46+
47+
public bool TryAddPublic(Transaction tx, DataCache snapshot)
48+
{
49+
var method = typeof(MemoryPool).GetMethod("TryAdd",
50+
BindingFlags.NonPublic | BindingFlags.Instance);
51+
return (bool)method.Invoke(this, new object[] { tx, snapshot });
52+
}
53+
}
54+
55+
/// <summary>
56+
/// A class that mimics the behavior of the internal PoolItem for MemoryPool benchmarks
57+
/// </summary>
58+
public class TestMemPoolItem : IComparable<TestMemPoolItem>
59+
{
60+
public readonly Transaction Tx;
61+
public readonly DateTime Timestamp;
62+
public DateTime LastBroadcastTimestamp;
63+
64+
public TestMemPoolItem(Transaction tx)
65+
{
66+
Tx = tx;
67+
Timestamp = DateTime.UtcNow;
68+
LastBroadcastTimestamp = Timestamp;
69+
}
70+
71+
public int CompareTo(TestMemPoolItem other)
72+
{
73+
if (other == null) return 1;
74+
75+
var ret = (Tx.GetAttribute<HighPriorityAttribute>() != null)
76+
.CompareTo(other.Tx.GetAttribute<HighPriorityAttribute>() != null);
77+
if (ret != 0) return ret;
78+
79+
// Fees sorted ascending
80+
ret = Tx.FeePerByte.CompareTo(other.Tx.FeePerByte);
81+
if (ret != 0) return ret;
82+
ret = Tx.NetworkFee.CompareTo(other.Tx.NetworkFee);
83+
if (ret != 0) return ret;
84+
85+
// Transaction hash sorted descending
86+
return other.Tx.Hash.CompareTo(Tx.Hash);
87+
}
88+
}
89+
90+
/// <summary>
91+
/// Reverse comparer for TestMemPoolItem, mimics the internal ReversePoolItemComparer
92+
/// </summary>
93+
public class ReverseTestMemPoolItemComparer : IComparer<TestMemPoolItem>
94+
{
95+
public static readonly ReverseTestMemPoolItemComparer Instance = new ReverseTestMemPoolItemComparer();
96+
97+
public int Compare(TestMemPoolItem x, TestMemPoolItem y)
98+
{
99+
if (y == null)
100+
{
101+
if (x == null) return 0;
102+
return -1;
103+
}
104+
105+
// Reverse value
106+
return y.CompareTo(x);
107+
}
108+
}
109+
110+
[MemoryDiagnoser]
111+
public class Benchmarks_MemoryPool
112+
{
113+
private const int NumTransactions = 200;
114+
private readonly Random random = new(42); // Fixed seed for reproducibility
115+
private readonly UInt160 senderAccount = UInt160.Zero;
116+
private List<TestMemPoolItem> poolItems;
117+
private SortedSet<TestMemPoolItem> reverseOrderedPoolItems;
118+
119+
[GlobalSetup]
120+
public void Setup()
121+
{
122+
// Generate test pool items with varying fees
123+
poolItems = new List<TestMemPoolItem>(NumTransactions);
124+
125+
for (int i = 0; i < NumTransactions; i++)
126+
{
127+
var tx = new Transaction
128+
{
129+
Version = 0,
130+
Nonce = (uint)random.Next(),
131+
ValidUntilBlock = (uint)random.Next(),
132+
Signers = new[] { new Signer { Account = senderAccount, Scopes = WitnessScope.None } },
133+
Attributes = Array.Empty<TransactionAttribute>(),
134+
Script = new byte[] { (byte)OpCode.RET },
135+
Witnesses = Array.Empty<Witness>(),
136+
// Set different fees to create variation
137+
NetworkFee = (i + 1) * 100,
138+
SystemFee = (NumTransactions - i) * 100
139+
};
140+
141+
// Create a pool item with the transaction
142+
poolItems.Add(new TestMemPoolItem(tx));
143+
}
144+
145+
// Pre-create the sorted set with ReverseTestMemPoolItemComparer
146+
reverseOrderedPoolItems = new SortedSet<TestMemPoolItem>(ReverseTestMemPoolItemComparer.Instance);
147+
}
148+
149+
[Benchmark]
150+
public void AddTransactions()
151+
{
152+
// Clear the set for each benchmark run
153+
reverseOrderedPoolItems.Clear();
154+
155+
// Add all transactions to the sorted set
156+
foreach (var item in poolItems)
157+
{
158+
reverseOrderedPoolItems.Add(item);
159+
}
160+
}
161+
162+
[Benchmark]
163+
public void RetrieveSortedTransactions()
164+
{
165+
// Clear and add items
166+
reverseOrderedPoolItems.Clear();
167+
foreach (var item in poolItems)
168+
{
169+
reverseOrderedPoolItems.Add(item);
170+
}
171+
172+
// Retrieve all transactions in reverse order (simulating GetSortedVerifiedTransactions)
173+
var result = new List<Transaction>(poolItems.Count);
174+
foreach (var item in reverseOrderedPoolItems)
175+
{
176+
result.Add(item.Tx);
177+
}
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)