Skip to content

Commit 1c43f7e

Browse files
authored
Call Contract Benchmarks (#1696)
1 parent 7bd1a49 commit 1c43f7e

File tree

2 files changed

+200
-1
lines changed

2 files changed

+200
-1
lines changed

x/contracts/runtime/import_contract_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,36 @@ import (
1313
"github.com/ava-labs/hypersdk/codec"
1414
)
1515

16+
func BenchmarkDeployContract(b *testing.B) {
17+
require := require.New(b)
18+
19+
ctx := context.Background()
20+
rt := newTestRuntime(ctx)
21+
contract, err := rt.newTestContract("deploy_contract")
22+
require.NoError(err)
23+
24+
runtime := contract.Runtime
25+
otherContractID := ids.GenerateTestID()
26+
err = runtime.AddContract(otherContractID[:], codec.CreateAddress(0, otherContractID), "call_contract")
27+
require.NoError(err)
28+
29+
b.ResetTimer()
30+
for i := 0; i < b.N; i++ {
31+
result, err := contract.Call(
32+
"deploy",
33+
otherContractID[:])
34+
require.NoError(err)
35+
36+
newAccount := into[codec.Address](result)
37+
38+
b.StopTimer()
39+
result, err = runtime.CallContract(newAccount, "simple_call")
40+
require.NoError(err)
41+
require.Equal(uint64(0), into[uint64](result))
42+
b.StartTimer()
43+
}
44+
}
45+
1646
func TestImportContractDeployContract(t *testing.T) {
1747
require := require.New(t)
1848
ctx := context.Background()

x/contracts/runtime/runtime_test.go

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,178 @@ import (
1212

1313
"github.com/ava-labs/hypersdk/codec"
1414
"github.com/ava-labs/hypersdk/consts"
15+
"github.com/ava-labs/hypersdk/x/contracts/test"
1516
)
1617

17-
// Benchmarks calling a contract that returns 0 immediately
18+
// Benchmarks the time it takes to get a module when it is not cached
19+
// BenchmarkRuntimeModuleNotCached-10 145 10262426 ns/op 245882 B/op 9 allocs/op
20+
func BenchmarkRuntimeModuleNotCached(b *testing.B) {
21+
require := require.New(b)
22+
23+
ctx := context.Background()
24+
rt := newTestRuntime(ctx)
25+
26+
contract, err := rt.newTestContract("simple")
27+
require.NoError(err)
28+
29+
callInfo := &CallInfo{
30+
Contract: contract.Address,
31+
State: rt.StateManager,
32+
FunctionName: "get_value",
33+
Params: test.SerializeParams(),
34+
}
35+
newInfo, err := rt.callContext.createCallInfo(callInfo)
36+
require.NoError(err)
37+
programID, err := callInfo.State.GetAccountContract(ctx, newInfo.Contract)
38+
require.NoError(err)
39+
40+
b.ResetTimer()
41+
for i := 0; i < b.N; i++ {
42+
_, err = rt.callContext.r.getModule(ctx, newInfo, programID)
43+
require.NoError(err)
44+
45+
b.StopTimer()
46+
// reset module
47+
rt.callContext.r.contractCache.Flush()
48+
b.StartTimer()
49+
}
50+
}
51+
52+
// Benchmarks the time it takes to get a module when it is cached
53+
// BenchmarkRuntimeModuleCached-10 2429497 495.1 ns/op 32 B/op 1 allocs/op
54+
func BenchmarkRuntimeModuleCached(b *testing.B) {
55+
require := require.New(b)
56+
57+
ctx, cancel := context.WithCancel(context.Background())
58+
defer cancel()
59+
60+
rt := newTestRuntime(ctx)
61+
contract, err := rt.newTestContract("simple")
62+
require.NoError(err)
63+
64+
result, err := contract.Call("get_value")
65+
require.NoError(err)
66+
require.Equal(uint64(0), into[uint64](result))
67+
68+
b.ResetTimer()
69+
70+
callInfo := &CallInfo{
71+
Contract: contract.Address,
72+
State: rt.StateManager,
73+
FunctionName: "get_value",
74+
Params: test.SerializeParams(),
75+
}
76+
newInfo, err := rt.callContext.createCallInfo(callInfo)
77+
require.NoError(err)
78+
programID, err := callInfo.State.GetAccountContract(ctx, newInfo.Contract)
79+
require.NoError(err)
80+
81+
for i := 0; i < b.N; i++ {
82+
_, err = rt.callContext.r.getModule(ctx, newInfo, programID)
83+
require.NoError(err)
84+
}
85+
}
86+
87+
// Benchmarks the time it takes to get an instance which happens on every contract call
88+
// Wasmitime rust allows you to pre-instantiate the module, which is not implemented in go
89+
//
90+
// https://docs.rs/wasmtime/latest/wasmtime/struct.Linker.html#method.instantiate_pre
91+
//
92+
// BenchmarkRuntimeInstance-10 48392 28894 ns/op 265 B/op 15 allocs/op
93+
func BenchmarkRuntimeInstance(b *testing.B) {
94+
require := require.New(b)
95+
96+
ctx := context.Background()
97+
rt := newTestRuntime(ctx)
98+
contract, err := rt.newTestContract("simple")
99+
require.NoError(err)
100+
101+
result, err := contract.Call("get_value")
102+
require.NoError(err)
103+
require.Equal(uint64(0), into[uint64](result))
104+
105+
b.ResetTimer()
106+
107+
callInfo := &CallInfo{
108+
Contract: contract.Address,
109+
State: rt.StateManager,
110+
FunctionName: "get_value",
111+
Params: test.SerializeParams(),
112+
}
113+
newInfo, err := rt.callContext.createCallInfo(callInfo)
114+
require.NoError(err)
115+
programID, err := newInfo.State.GetAccountContract(ctx, newInfo.Contract)
116+
require.NoError(err)
117+
118+
module, err := rt.callContext.r.getModule(ctx, newInfo, programID)
119+
require.NoError(err)
120+
b.ResetTimer()
121+
for i := 0; i < b.N; i++ {
122+
inst, err := rt.callContext.r.getInstance(module, rt.callContext.r.hostImports)
123+
require.NoError(err)
124+
_ = inst
125+
126+
b.StopTimer()
127+
// reset module
128+
module, err = rt.callContext.r.getModule(ctx, newInfo, programID)
129+
require.NoError(err)
130+
b.StartTimer()
131+
}
132+
}
133+
134+
// Benchmarks the time it takes to call a contract with everything instantiated
135+
// BenchmarkRuntimeInstanceCall-10 32728 33143 ns/op 1629 B/op 191 allocs/op
136+
func BenchmarkRuntimeInstanceCall(b *testing.B) {
137+
require := require.New(b)
138+
ctx := context.Background()
139+
140+
rt := newTestRuntime(ctx)
141+
contract, err := rt.newTestContract("simple")
142+
require.NoError(err)
143+
144+
result, err := contract.Call("get_value")
145+
require.NoError(err)
146+
require.Equal(uint64(0), into[uint64](result))
147+
148+
b.ResetTimer()
149+
150+
callInfo := &CallInfo{
151+
Contract: contract.Address,
152+
State: rt.StateManager,
153+
FunctionName: "get_value",
154+
Params: test.SerializeParams(),
155+
}
156+
newInfo, err := rt.callContext.createCallInfo(callInfo)
157+
require.NoError(err)
158+
programID, err := newInfo.State.GetAccountContract(ctx, newInfo.Contract)
159+
require.NoError(err)
160+
161+
module, err := rt.callContext.r.getModule(ctx, newInfo, programID)
162+
require.NoError(err)
163+
inst, err := rt.callContext.r.getInstance(module, rt.callContext.r.hostImports)
164+
require.NoError(err)
165+
b.ResetTimer()
166+
newInfo.inst = inst
167+
rt.callContext.r.setCallInfo(inst.store, newInfo)
168+
169+
for i := 0; i < b.N; i++ {
170+
result, err := inst.call(ctx, newInfo)
171+
require.NoError(err)
172+
_ = result
173+
174+
rt.callContext.r.deleteCallInfo(inst.store)
175+
b.StopTimer()
176+
// reset module
177+
module, err = rt.callContext.r.getModule(ctx, newInfo, programID)
178+
require.NoError(err)
179+
inst, err = rt.callContext.r.getInstance(module, rt.callContext.r.hostImports)
180+
require.NoError(err)
181+
newInfo.inst = inst
182+
b.StartTimer()
183+
rt.callContext.r.setCallInfo(inst.store, newInfo)
184+
}
185+
}
186+
18187
func BenchmarkRuntimeCallContractBasic(b *testing.B) {
19188
require := require.New(b)
20189
ctx := context.Background()

0 commit comments

Comments
 (0)