-
Notifications
You must be signed in to change notification settings - Fork 1k
perf: cache native dispatch invokers #4518
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
Open
Jim8y
wants to merge
5
commits into
master-n3
Choose a base branch
from
fix-native-dispatch-delegates
base: master-n3
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
98f6f59
perf: cache native dispatch invokers
Jim8y 66c6a0f
bench: add native dispatch benchmarks
Jim8y ee905e2
Merge branch 'master-n3' into fix-native-dispatch-delegates
shargon 9d993ee
Merge branch 'master-n3' into fix-native-dispatch-delegates
ajara87 6a31470
Merge branch 'master-n3' into fix-native-dispatch-delegates
ajara87 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
benchmarks/Neo.Benchmarks/SmartContract/Benchmarks.Dispatch.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| // Copyright (C) 2015-2026 The Neo Project. | ||
| // | ||
| // Benchmarks.Dispatch.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.Configs; | ||
| using Neo.Persistence; | ||
| using Neo.SmartContract.Manifest; | ||
| using Neo.SmartContract.Native; | ||
| using Neo.VM; | ||
| using Neo.VM.Types; | ||
| using System.Numerics; | ||
| using System.Reflection; | ||
|
|
||
| namespace Neo.SmartContract.Benchmark | ||
| { | ||
| [Config(typeof(BenchmarkConfig))] | ||
| [MemoryDiagnoser] | ||
| public class Benchmarks_Dispatch | ||
| { | ||
| private const int DispatchCount = 1000; | ||
| private static readonly ProtocolSettings s_protocol = ProtocolSettings.Load("config.json"); | ||
| private static readonly NeoSystem s_system = new(s_protocol, (string)null); | ||
| private static readonly Script s_dummyScript = new(new byte[] { (byte)OpCode.RET }, true); | ||
| private static readonly InteropDescriptor s_syscallNoArgs = CreateInteropDescriptor("InteropNoArgs"); | ||
| private static readonly InteropDescriptor s_syscallOneArg = CreateInteropDescriptor("InteropOneArg"); | ||
| private static readonly BenchmarkNativeContract s_nativeContract = new(); | ||
| private static readonly ContractState s_nativeContractState = s_nativeContract.GetContractState(s_protocol, 0); | ||
| private static readonly ContractMethodDescriptor s_nativeNoArgMethod = s_nativeContractState.Manifest.Abi.GetMethod("noArg", 0)!; | ||
| private static readonly ContractMethodDescriptor s_nativeOneArgMethod = s_nativeContractState.Manifest.Abi.GetMethod("oneArg", 1)!; | ||
| private static readonly StackItem s_integerArg = new Integer(42); | ||
|
|
||
| [Benchmark(OperationsPerInvoke = DispatchCount)] | ||
| public int Syscall_NoArgs() | ||
| { | ||
| using var snapshot = s_system.GetSnapshotCache(); | ||
| using var engine = new BenchmarkEngine(snapshot); | ||
| engine.PrepareInteropContext(); | ||
|
|
||
| int result = 0; | ||
| for (int i = 0; i < DispatchCount; i++) | ||
| result = engine.InvokeInterop(s_syscallNoArgs); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| [Benchmark(OperationsPerInvoke = DispatchCount)] | ||
| public int Syscall_OneArg() | ||
| { | ||
| using var snapshot = s_system.GetSnapshotCache(); | ||
| using var engine = new BenchmarkEngine(snapshot); | ||
| engine.PrepareInteropContext(); | ||
|
|
||
| int result = 0; | ||
| for (int i = 0; i < DispatchCount; i++) | ||
| result = engine.InvokeInterop(s_syscallOneArg, s_integerArg); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| [Benchmark(OperationsPerInvoke = DispatchCount)] | ||
| public int Native_NoArgs() | ||
| { | ||
| using var snapshot = s_system.GetSnapshotCache(); | ||
| using var engine = new BenchmarkEngine(snapshot); | ||
| engine.PrepareNativeContext(s_nativeContractState, s_nativeNoArgMethod); | ||
|
|
||
| int result = 0; | ||
| for (int i = 0; i < DispatchCount; i++) | ||
| result = engine.InvokeNative(); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| [Benchmark(OperationsPerInvoke = DispatchCount)] | ||
| public int Native_OneArg() | ||
| { | ||
| using var snapshot = s_system.GetSnapshotCache(); | ||
| using var engine = new BenchmarkEngine(snapshot); | ||
| engine.PrepareNativeContext(s_nativeContractState, s_nativeOneArgMethod); | ||
|
|
||
| int result = 0; | ||
| for (int i = 0; i < DispatchCount; i++) | ||
| result = engine.InvokeNative(s_integerArg); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| private static InteropDescriptor CreateInteropDescriptor(string methodName) | ||
| { | ||
| var flags = BindingFlags.Instance | BindingFlags.NonPublic; | ||
| var method = typeof(BenchmarkEngine).GetMethod(methodName, flags)!; | ||
|
|
||
| return new InteropDescriptor | ||
| { | ||
| Name = $"Benchmark.{methodName}", | ||
| Handler = method, | ||
| FixedPrice = 0, | ||
| RequiredCallFlags = CallFlags.None | ||
| }; | ||
| } | ||
|
|
||
| private sealed class BenchmarkConfig : ManualConfig | ||
| { | ||
| public BenchmarkConfig() | ||
| { | ||
| Options |= ConfigOptions.DisableOptimizationsValidator; | ||
| } | ||
| } | ||
|
|
||
| private sealed class BenchmarkEngine(DataCache snapshot) : ApplicationEngine(TriggerType.Application, null, snapshot, s_system.GenesisBlock, s_protocol, long.MaxValue / FeeFactor) | ||
| { | ||
| public void PrepareInteropContext() | ||
| { | ||
| LoadScript(s_dummyScript, configureState: state => state.CallFlags = CallFlags.All); | ||
| } | ||
|
|
||
| public void PrepareNativeContext(ContractState contract, ContractMethodDescriptor method) | ||
| { | ||
| LoadScript(contract.Script, initialPosition: method.Offset + 1, configureState: state => | ||
| { | ||
| state.CallFlags = CallFlags.All; | ||
| state.ScriptHash = contract.Hash; | ||
| state.Contract = contract; | ||
| }); | ||
| } | ||
|
|
||
| public int InvokeInterop(InteropDescriptor descriptor, params StackItem[] args) | ||
| { | ||
| PushArguments(args); | ||
| OnSysCall(descriptor); | ||
| return PopIntegerResult(); | ||
| } | ||
|
|
||
| public int InvokeNative(params StackItem[] args) | ||
| { | ||
| PushArguments(args); | ||
| CallNativeContract(0); | ||
| return PopIntegerResult(); | ||
| } | ||
|
|
||
| private void PushArguments(StackItem[] args) | ||
| { | ||
| for (int i = args.Length - 1; i >= 0; i--) | ||
| CurrentContext!.EvaluationStack.Push(args[i]); | ||
| } | ||
|
|
||
| private int PopIntegerResult() | ||
| { | ||
| return (int)CurrentContext!.EvaluationStack.Pop().GetInteger(); | ||
| } | ||
|
|
||
| private int InteropNoArgs() => 1; | ||
|
|
||
| private int InteropOneArg(int value) => value + 1; | ||
| } | ||
|
|
||
| private sealed class BenchmarkNativeContract : NativeContract | ||
| { | ||
| [ContractMethod] | ||
| private static int NoArg() => 1; | ||
|
|
||
| [ContractMethod] | ||
| private static int OneArg(int value) => value + 1; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merge these two
if?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree