Skip to content

Commit 3093541

Browse files
committed
test(benchmark): initial benchmarks for ledger and database
Signed-off-by: Chris Gianelloni <wolf31o2@blinklabs.io>
1 parent 1819090 commit 3093541

File tree

10 files changed

+2711
-11
lines changed

10 files changed

+2711
-11
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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Dingo Ledger & Database Benchmark Results
2+
3+
## Latest Results
4+
5+
### Test Environment
6+
- **Date**: November 25, 2025
7+
- **Go Version**:
8+
- **OS**:
9+
- **Architecture**:
10+
- **CPU Cores**:
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 | 29991 | 37099ns | 4KB | 79 |
20+
| Pool Registration Lookups No Data | 22206 | 52023ns | 10KB | 93 |
21+
| Account Lookup By Stake Key Real Data | 38986 | 33545ns | 4KB | 75 |
22+
| Utxo Lookup By Address No Data | 67723 | 17215ns | 2KB | 19 |
23+
| Transaction Validation | 228741294 | 5.264ns | 0B | 0 |
24+
| Real Block Reading | 21 | 51664132ns | 2183KB | 74472 |
25+
| Block Retrieval By Index Real Data | 271734 | 4715ns | 472B | 11 |
26+
| Index Building Time | 14127 | 86548ns | 17KB | 119 |
27+
| Chain Sync From Genesis | 79 | 15385684ns | 100.0blocks_processed | 2248466 |
28+
| Block Retrieval By Index No Data | 279624 | 4125ns | 472B | 11 |
29+
| Transaction Create | 98858 | 16169ns | 2KB | 18 |
30+
| Utxo Lookup By Address Real Data | 84540 | 16915ns | 2KB | 19 |
31+
| Era Transition Performance | 516050931 | 2.324ns | 0B | 0 |
32+
| Protocol Parameters Lookup By Epoch Real Data | 44349 | 30968ns | 5KB | 62 |
33+
| Pool Registration Lookups Real Data | 23996 | 46528ns | 10KB | 93 |
34+
| Stake Registration Lookups Real Data | 35199 | 36428ns | 5KB | 69 |
35+
| Era Transition Performance Real Data | 5455 | 298862ns | 83KB | 490 |
36+
| Real Block Processing | 13333 | 90244ns | 17KB | 119 |
37+
| Utxo Lookup By Ref No Data | 10224 | 116850ns | 8KB | 131 |
38+
| Stake Registration Lookups No Data | 32708 | 34519ns | 5KB | 69 |
39+
| Utxo Lookup By Ref Real Data | 10000 | 116585ns | 8KB | 131 |
40+
| Protocol Parameters Lookup By Epoch No Data | 40987 | 34100ns | 5KB | 62 |
41+
| Datum Lookup By Hash No Data | 38138 | 32431ns | 4KB | 69 |
42+
| Block Processing Throughput | 10000 | 165400ns | 6046blocks/sec | 18286 |
43+
| Real Data Queries | 91810 | 18465ns | 5KB | 43 |
44+
| Block Nonce Lookup Real Data | 34676 | 38519ns | 4KB | 73 |
45+
| Pool Lookup By Key Hash Real Data | 39112 | 33676ns | 4KB | 79 |
46+
| D Rep Lookup By Key Hash No Data | 35720 | 33017ns | 4KB | 77 |
47+
| Transaction History Queries No Data | 35151 | 35753ns | 4KB | 78 |
48+
| Datum Lookup By Hash Real Data | 44521 | 32613ns | 4KB | 69 |
49+
| Block Nonce Lookup No Data | 31076 | 38415ns | 4KB | 73 |
50+
| Account Lookup By Stake Key No Data | 34257 | 33124ns | 4KB | 75 |
51+
| Transaction History Queries Real Data | 37896 | 39330ns | 4KB | 78 |
52+
| Concurrent Queries | 19658 | 58155ns | 172221queries/sec | 3851 |
53+
| D Rep Lookup By Key Hash Real Data | 39349 | 35230ns | 4KB | 77 |
54+
| Block Memory Usage | 25939 | 48725ns | 14KB | 49 |
55+
## Performance Changes
56+
57+
No previous results found. This is the first benchmark run.
58+

cmd/dingo/main.go

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"fmt"
1919
"log/slog"
2020
"os"
21+
"runtime/pprof"
22+
"strings"
2123

2224
"github.com/blinklabs-io/dingo/internal/config"
2325
"github.com/blinklabs-io/dingo/internal/version"
@@ -37,7 +39,9 @@ func slogPrintf(format string, v ...any) {
3739

3840
var (
3941
globalFlags = struct {
40-
debug bool
42+
cpuprofile string
43+
memprofile string
44+
debug bool
4145
}{}
4246
configFile string
4347
)
@@ -72,6 +76,50 @@ func commonRun() *slog.Logger {
7276
}
7377

7478
func main() {
79+
// Parse profiling flags before cobra setup (handle both --flag=value and --flag value syntax)
80+
cpuprofile := ""
81+
memprofile := ""
82+
args := os.Args
83+
if len(args) > 0 {
84+
args = args[1:] // Skip program name
85+
} else {
86+
args = []string{}
87+
}
88+
for i := 0; i < len(args); i++ {
89+
arg := args[i]
90+
switch {
91+
case strings.HasPrefix(arg, "--cpuprofile="):
92+
cpuprofile = strings.TrimPrefix(arg, "--cpuprofile=")
93+
case arg == "--cpuprofile" && i+1 < len(args):
94+
cpuprofile = args[i+1]
95+
i++ // Skip next arg
96+
case strings.HasPrefix(arg, "--memprofile="):
97+
memprofile = strings.TrimPrefix(arg, "--memprofile=")
98+
case arg == "--memprofile" && i+1 < len(args):
99+
memprofile = args[i+1]
100+
i++ // Skip next arg
101+
}
102+
}
103+
104+
// Initialize CPU profiling (starts immediately, stops on exit)
105+
if cpuprofile != "" {
106+
fmt.Fprintf(os.Stderr, "Starting CPU profiling to %s\n", cpuprofile)
107+
f, err := os.Create(cpuprofile)
108+
if err != nil {
109+
fmt.Fprintf(os.Stderr, "could not create CPU profile: %v\n", err)
110+
os.Exit(1)
111+
}
112+
defer f.Close()
113+
if err := pprof.StartCPUProfile(f); err != nil {
114+
fmt.Fprintf(os.Stderr, "could not start CPU profile: %v\n", err)
115+
os.Exit(1)
116+
}
117+
defer func() {
118+
pprof.StopCPUProfile()
119+
fmt.Fprintf(os.Stderr, "CPU profiling stopped\n")
120+
}()
121+
}
122+
75123
rootCmd := &cobra.Command{
76124
Use: programName,
77125
Run: func(cmd *cobra.Command, args []string) {
@@ -89,6 +137,10 @@ func main() {
89137
BoolVarP(&globalFlags.debug, "debug", "D", false, "enable debug logging")
90138
rootCmd.PersistentFlags().
91139
StringVar(&configFile, "config", "", "path to config file")
140+
rootCmd.PersistentFlags().
141+
StringVar(&globalFlags.cpuprofile, "cpuprofile", "", "write cpu profile to file")
142+
rootCmd.PersistentFlags().
143+
StringVar(&globalFlags.memprofile, "memprofile", "", "write memory profile to file")
92144

93145
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
94146
cfg, err := config.LoadConfig(configFile)
@@ -109,4 +161,19 @@ func main() {
109161
// NOTE: we purposely don't display the error, since cobra will have already displayed it
110162
os.Exit(1)
111163
}
164+
165+
// Finalize memory profiling (captures heap state at program end)
166+
if memprofile != "" {
167+
f, err := os.Create(memprofile)
168+
if err != nil {
169+
fmt.Fprintf(os.Stderr, "could not create memory profile: %v\n", err)
170+
os.Exit(1)
171+
}
172+
defer f.Close()
173+
if err := pprof.WriteHeapProfile(f); err != nil {
174+
fmt.Fprintf(os.Stderr, "could not write memory profile: %v\n", err)
175+
os.Exit(1)
176+
}
177+
fmt.Fprintf(os.Stderr, "Memory profiling complete\n")
178+
}
112179
}

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+
}

database/plugin/blob/aws/commit_timestamp.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ func (b *BlobStoreS3) GetCommitTimestamp(ctx context.Context) (int64, error) {
3838
err,
3939
)
4040
if migrateErr := b.SetCommitTimestamp(ctx, ts); migrateErr != nil {
41-
b.logger.Errorf("failed to migrate plaintext commit timestamp: %v", migrateErr)
41+
b.logger.Errorf(
42+
"failed to migrate plaintext commit timestamp: %v",
43+
migrateErr,
44+
)
4245
}
4346
return ts, nil
4447
}

database/plugin/blob/gcs/commit_timestamp.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ func (b *BlobStoreGCS) GetCommitTimestamp(ctx context.Context) (int64, error) {
4848
err,
4949
)
5050
if migrateErr := b.SetCommitTimestamp(ctx, ts); migrateErr != nil {
51-
b.logger.Errorf("failed to migrate plaintext commit timestamp: %v", migrateErr)
51+
b.logger.Errorf(
52+
"failed to migrate plaintext commit timestamp: %v",
53+
migrateErr,
54+
)
5255
}
5356
return ts, nil
5457
}

database/plugin/metadata/sqlite/plugin.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ import (
2020

2121
type SqliteOptionFunc func(*MetadataStoreSqlite)
2222

23-
//nolint:unused
24-
var cmdLineOptions struct {
25-
dataDir string
26-
}
27-
2823
// Register plugin
2924
func init() {
3025
plugin.Register(

database/plugin/metadata/sqlite/transaction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func saveAccountIfNew(account *models.Account, txn *gorm.DB) error {
105105
}
106106

107107
// saveCertRecord saves a certificate record and returns any error
108-
func saveCertRecord(record interface{}, txn *gorm.DB) error {
108+
func saveCertRecord(record any, txn *gorm.DB) error {
109109
result := txn.Create(record)
110110
return result.Error
111111
}

0 commit comments

Comments
 (0)