Skip to content

Commit f2b4300

Browse files
authored
test(benchmark): initial benchmarks for ledger and database (#1059)
Signed-off-by: Chris Gianelloni <[email protected]>
1 parent 23af5a5 commit f2b4300

File tree

6 files changed

+2801
-4
lines changed

6 files changed

+2801
-4
lines changed

Makefile

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ GOMODULE=$(shell grep ^module $(ROOT_DIR)/go.mod | awk '{ print $$2 }')
1313
# Set version strings based on git tag and current ref
1414
GO_LDFLAGS=-ldflags "-s -w -X '$(GOMODULE)/internal/version.Version=$(shell git describe --tags --exact-match 2>/dev/null)' -X '$(GOMODULE)/internal/version.CommitHash=$(shell git rev-parse --short HEAD)'"
1515

16-
.PHONY: build mod-tidy clean format golines test
16+
.PHONY: all build mod-tidy clean format golines test bench test-load-profile
1717

18-
# Alias for building program binary
18+
# Default target
19+
all: build test bench
20+
21+
# Build target
1922
build: $(BINARIES)
2023

2124
# Builds and installs binary in ~/.local/bin
@@ -43,10 +46,19 @@ golines:
4346
test: mod-tidy
4447
go test -v -race ./...
4548

49+
bench: mod-tidy
50+
go test -run=^$$ -bench=. -benchmem ./...
51+
4652
test-load:
4753
rm -rf .dingo
4854
go run ./cmd/dingo load database/immutable/testdata
4955

56+
test-load-profile:
57+
rm -rf .dingo dingo
58+
go build -o dingo ./cmd/dingo
59+
./dingo --cpuprofile=cpu.prof --memprofile=mem.prof load database/immutable/testdata
60+
@echo "Profiling complete. Run 'go tool pprof cpu.prof' or 'go tool pprof mem.prof' to analyze"
61+
5062
# Build our program binaries
5163
# Depends on GO_FILES to determine when rebuild is needed
5264
$(BINARIES): mod-tidy $(GO_FILES)

benchmark_results.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Dingo Ledger & Database Benchmark Results
2+
3+
## Latest Results
4+
5+
### Test Environment
6+
- **Date**: November 26, 2025
7+
- **Go Version**: 1.24.1
8+
- **OS**: Linux
9+
- **Architecture**: aarch64
10+
- **CPU Cores**: 128
11+
- **Data Source**: Real Cardano preview testnet data (40k+ blocks, slots 0-863,996)
12+
13+
### Benchmark Results
14+
15+
All benchmarks run with `-benchmem` flag showing memory allocations and operation counts.
16+
17+
| Benchmark | Operations/sec | Time/op | Memory/op | Allocs/op |
18+
|-----------|----------------|---------|-----------|-----------|
19+
| Pool Lookup By Key Hash No Data | 36231 | 33604ns | 4KB | 79 |
20+
| Pool Registration Lookups No Data | 24210 | 46595ns | 10KB | 93 |
21+
| Account Lookup By Stake Key Real Data | 39950 | 34026ns | 4KB | 75 |
22+
| Utxo Lookup By Address No Data | 109825 | 16460ns | 2KB | 19 |
23+
| Storage Backends/memory | 32593 | 34818ns | 13KB | 70 |
24+
| Transaction Validation | 230958193 | 5.238ns | 0B | 0 |
25+
| Real Block Reading | 22 | 53427289ns | 2183KB | 74472 |
26+
| Block Retrieval By Index Real Data | 303981 | 3868ns | 472B | 11 |
27+
| Index Building Time | 14078 | 87080ns | 17KB | 119 |
28+
| Chain Sync From Genesis | 74 | 15096067ns | 100.0blocks_processed | 2247880 |
29+
| Block Retrieval By Index No Data | 315110 | 3652ns | 472B | 11 |
30+
| Transaction Create | 77013 | 16824ns | 2KB | 18 |
31+
| Utxo Lookup By Address Real Data | 82125 | 16014ns | 2KB | 19 |
32+
| Era Transition Performance | 459890545 | 2.178ns | 0B | 0 |
33+
| Protocol Parameters Lookup By Epoch Real Data | 44181 | 32903ns | 5KB | 62 |
34+
| Pool Registration Lookups Real Data | 22616 | 48729ns | 10KB | 93 |
35+
| Stake Registration Lookups Real Data | 38761 | 33724ns | 5KB | 69 |
36+
| Era Transition Performance Real Data | 4340 | 267246ns | 83KB | 490 |
37+
| Real Block Processing | 13651 | 84993ns | 17KB | 119 |
38+
| Utxo Lookup By Ref No Data | 10461 | 119506ns | 8KB | 131 |
39+
| Storage Backends/disk | 33004 | 32561ns | 13KB | 70 |
40+
| Test Load/memory | 1912 | 650898ns | 260KB | 1400 |
41+
| Stake Registration Lookups No Data | 33963 | 33887ns | 5KB | 69 |
42+
| Utxo Lookup By Ref Real Data | 10000 | 122404ns | 8KB | 131 |
43+
| Protocol Parameters Lookup By Epoch No Data | 37996 | 32298ns | 5KB | 62 |
44+
| Datum Lookup By Hash No Data | 32635 | 34072ns | 4KB | 69 |
45+
| Block Processing Throughput | 3904 | 290370ns | 3444blocks/sec | 22462 |
46+
| Real Data Queries | 68570 | 17752ns | 5KB | 43 |
47+
| Block Nonce Lookup Real Data | 34851 | 37815ns | 4KB | 73 |
48+
| Test Load/disk | 2068 | 536686ns | 260KB | 1400 |
49+
| Pool Lookup By Key Hash Real Data | 40214 | 33631ns | 4KB | 79 |
50+
| D Rep Lookup By Key Hash No Data | 35266 | 36094ns | 4KB | 77 |
51+
| Transaction History Queries No Data | 34167 | 33889ns | 4KB | 78 |
52+
| Datum Lookup By Hash Real Data | 44810 | 33686ns | 4KB | 69 |
53+
| Block Nonce Lookup No Data | 32011 | 36515ns | 4KB | 73 |
54+
| Account Lookup By Stake Key No Data | 34898 | 35142ns | 4KB | 75 |
55+
| Transaction History Queries Real Data | 37575 | 34812ns | 4KB | 78 |
56+
| Concurrent Queries | 15408 | 68781ns | 14581queries/sec | 3943 |
57+
| D Rep Lookup By Key Hash Real Data | 39360 | 34907ns | 4KB | 77 |
58+
| Block Memory Usage | 29292 | 44369ns | 14KB | 49 |
59+
60+
## Performance Changes
61+
62+
Changes since **November 26, 2025**:
63+
64+
### Summary
65+
- **Faster benchmarks**: 19
66+
- **Slower benchmarks**: 20
67+
- **New benchmarks**: 0
68+
- **Removed benchmarks**: 0
69+
70+
### Top Improvements
71+
- Utxo Lookup By Address No Data (+44%)
72+
- Transaction Validation (+0%)
73+
- Test Load/memory (+19%)
74+
- Test Load/disk (+31%)
75+
- Storage Backends/memory (+3%)
76+
77+
### Performance Regressions
78+
- Pool Lookup By Key Hash No Data (-0%)
79+
- Index Building Time (-0%)
80+
- Account Lookup By Stake Key No Data (-0%)
81+
- Transaction History Queries No Data (-1%)
82+
- Stake Registration Lookups No Data (-1%)
83+
84+
85+
## Historical Results
86+
87+
### November 26, 2025
88+
89+
| Benchmark | Operations/sec | Time/op | Memory/op | Allocs/op |
90+
|-----------|----------------|---------|-----------|-----------|
91+
| Pool Lookup By Key Hash No Data | 36328 | 33556ns | 4KB | 79 |
92+
| Pool Registration Lookups No Data | 24898 | 51298ns | 10KB | 93 |
93+
| Account Lookup By Stake Key Real Data | 39936 | 33871ns | 4KB | 75 |
94+
| Utxo Lookup By Address No Data | 75966 | 16953ns | 2KB | 19 |
95+
| Storage Backends/memory | 31369 | 33394ns | 13KB | 70 |
96+
| Transaction Validation | 230637091 | 5.213ns | 0B | 0 |
97+
| Real Block Reading | 20 | 55046083ns | 2183KB | 74472 |
98+
| Block Retrieval By Index Real Data | 312492 | 4102ns | 472B | 11 |
99+
| Index Building Time | 14168 | 86378ns | 17KB | 119 |
100+
| Chain Sync From Genesis | 100 | 14189212ns | 100.0blocks_processed | 2247966 |
101+
| Block Retrieval By Index No Data | 277410 | 4025ns | 472B | 11 |
102+
| Transaction Create | 92761 | 16456ns | 2KB | 18 |
103+
| Utxo Lookup By Address Real Data | 86467 | 15705ns | 2KB | 19 |
104+
| Era Transition Performance | 499031763 | 2.139ns | 0B | 0 |
105+
| Protocol Parameters Lookup By Epoch Real Data | 44150 | 30066ns | 5KB | 62 |
106+
| Pool Registration Lookups Real Data | 23724 | 48201ns | 10KB | 93 |
107+
| Stake Registration Lookups Real Data | 40082 | 32844ns | 5KB | 69 |
108+
| Era Transition Performance Real Data | 3525 | 305364ns | 83KB | 490 |
109+
| Real Block Processing | 13509 | 87846ns | 17KB | 119 |
110+
| Utxo Lookup By Ref No Data | 10724 | 111792ns | 8KB | 131 |
111+
| Storage Backends/disk | 28960 | 37643ns | 13KB | 70 |
112+
| Test Load/memory | 1605 | 669272ns | 260KB | 1400 |
113+
| Stake Registration Lookups No Data | 34359 | 36417ns | 5KB | 69 |
114+
| Utxo Lookup By Ref Real Data | 10000 | 111756ns | 8KB | 131 |
115+
| Protocol Parameters Lookup By Epoch No Data | 37886 | 32905ns | 5KB | 62 |
116+
| Datum Lookup By Hash No Data | 40302 | 29076ns | 4KB | 69 |
117+
| Block Processing Throughput | 4632 | 287053ns | 3484blocks/sec | 22466 |
118+
| Real Data Queries | 62532 | 19011ns | 5KB | 43 |
119+
| Block Nonce Lookup Real Data | 35782 | 38514ns | 4KB | 73 |
120+
| Test Load/disk | 1574 | 711525ns | 260KB | 1400 |
121+
| Pool Lookup By Key Hash Real Data | 39812 | 33599ns | 4KB | 79 |
122+
| D Rep Lookup By Key Hash No Data | 35139 | 35088ns | 4KB | 77 |
123+
| Transaction History Queries No Data | 34530 | 36887ns | 4KB | 78 |
124+
| Datum Lookup By Hash Real Data | 36972 | 31369ns | 4KB | 69 |
125+
| Block Nonce Lookup No Data | 32977 | 36460ns | 4KB | 73 |
126+
| Account Lookup By Stake Key No Data | 35020 | 32890ns | 4KB | 75 |
127+
| Transaction History Queries Real Data | 38739 | 35904ns | 4KB | 78 |
128+
| D Rep Lookup By Key Hash Real Data | 38977 | 36859ns | 4KB | 77 |
129+
| Concurrent Queries | 19104 | 59527ns | 168280queries/sec | 3851 |
130+
| Block Memory Usage | 26552 | 46212ns | 14KB | 49 |

cmd/dingo/main.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"log/slog"
2020
"os"
21+
"runtime/pprof"
2122
"strings"
2223

2324
"github.com/blinklabs-io/dingo/database/plugin"
@@ -137,6 +138,50 @@ func listCommand() *cobra.Command {
137138
}
138139

139140
func main() {
141+
// Parse profiling flags before cobra setup (handle both --flag=value and --flag value syntax)
142+
cpuprofile := ""
143+
memprofile := ""
144+
args := os.Args
145+
if len(args) > 0 {
146+
args = args[1:] // Skip program name
147+
} else {
148+
args = []string{}
149+
}
150+
for i := 0; i < len(args); i++ {
151+
arg := args[i]
152+
switch {
153+
case strings.HasPrefix(arg, "--cpuprofile="):
154+
cpuprofile = strings.TrimPrefix(arg, "--cpuprofile=")
155+
case arg == "--cpuprofile" && i+1 < len(args):
156+
cpuprofile = args[i+1]
157+
i++ // Skip next arg
158+
case strings.HasPrefix(arg, "--memprofile="):
159+
memprofile = strings.TrimPrefix(arg, "--memprofile=")
160+
case arg == "--memprofile" && i+1 < len(args):
161+
memprofile = args[i+1]
162+
i++ // Skip next arg
163+
}
164+
}
165+
166+
// Initialize CPU profiling (starts immediately, stops on exit)
167+
if cpuprofile != "" {
168+
fmt.Fprintf(os.Stderr, "Starting CPU profiling to %s\n", cpuprofile)
169+
f, err := os.Create(cpuprofile)
170+
if err != nil {
171+
fmt.Fprintf(os.Stderr, "could not create CPU profile: %v\n", err)
172+
os.Exit(1)
173+
}
174+
defer f.Close()
175+
if err := pprof.StartCPUProfile(f); err != nil {
176+
fmt.Fprintf(os.Stderr, "could not start CPU profile: %v\n", err)
177+
os.Exit(1)
178+
}
179+
defer func() {
180+
pprof.StopCPUProfile()
181+
fmt.Fprintf(os.Stderr, "CPU profiling stopped\n")
182+
}()
183+
}
184+
140185
rootCmd := &cobra.Command{
141186
Use: programName,
142187
Run: func(cmd *cobra.Command, args []string) {
@@ -200,8 +245,27 @@ func main() {
200245
rootCmd.AddCommand(versionCommand())
201246

202247
// Execute cobra command
248+
exitCode := 0
203249
if err := rootCmd.Execute(); err != nil {
204-
// NOTE: we purposely don't display the error, since cobra will have already displayed it
205-
os.Exit(1)
250+
exitCode = 1
251+
}
252+
253+
// Finalize memory profiling before exit
254+
if memprofile != "" {
255+
f, err := os.Create(memprofile)
256+
if err != nil {
257+
fmt.Fprintf(os.Stderr, "could not create memory profile: %v\n", err)
258+
} else {
259+
if err := pprof.WriteHeapProfile(f); err != nil {
260+
fmt.Fprintf(os.Stderr, "could not write memory profile: %v\n", err)
261+
} else {
262+
fmt.Fprintf(os.Stderr, "Memory profiling complete\n")
263+
}
264+
f.Close()
265+
}
266+
}
267+
268+
if exitCode != 0 {
269+
os.Exit(exitCode)
206270
}
207271
}

database/benchmark_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 Blink Labs Software
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package database
16+
17+
import (
18+
"testing"
19+
)
20+
21+
// BenchmarkTransactionCreate benchmarks creating a read-only transaction
22+
func BenchmarkTransactionCreate(b *testing.B) {
23+
// Create a temporary database
24+
config := &Config{
25+
DataDir: "", // In-memory
26+
}
27+
db, err := New(config)
28+
if err != nil {
29+
b.Fatal(err)
30+
}
31+
defer db.Close()
32+
33+
b.ResetTimer() // Reset timer after setup
34+
for b.Loop() {
35+
txn := db.Transaction(false)
36+
if err := txn.Commit(); err != nil {
37+
b.Fatal(err)
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)