Skip to content

Commit 9e11f1e

Browse files
committed
Simple benchmarking tool
1 parent c64128e commit 9e11f1e

File tree

5 files changed

+311
-0
lines changed

5 files changed

+311
-0
lines changed

Makefile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Benchmark targets
2+
.PHONY: benchmark
3+
benchmark: build-benchmark
4+
@echo "Running benchmark..."
5+
./bin/benchmark $(ARGS)
6+
7+
.PHONY: benchmark-v1
8+
benchmark-v1: build-benchmark
9+
@echo "Running v1 HTTP benchmark only..."
10+
./bin/benchmark -v1 -v2=false $(ARGS)
11+
12+
.PHONY: benchmark-v2
13+
benchmark-v2: build-benchmark
14+
@echo "Running v2 gRPC benchmark only..."
15+
./bin/benchmark -v1=false -v2 $(ARGS)
16+
17+
.PHONY: build-benchmark
18+
build-benchmark:
19+
@echo "Building benchmark tool..."
20+
@mkdir -p bin
21+
go build -o bin/benchmark ./cmd/benchmark
22+
23+
# Example usage:
24+
# make benchmark ARGS="-startheight=100 -endheight=200"
25+
# make benchmark-v1 ARGS="-startheight=100 -endheight=200"
26+
# make benchmark-v2 ARGS="-startheight=100 -endheight=200"

cmd/benchmark/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Oracle Benchmark Tool
2+
3+
This tool benchmarks the performance difference between v1 (HTTP) and v2 (gRPC streaming) APIs for fetching block data.
4+
5+
## Building
6+
7+
```bash
8+
make build-benchmark
9+
```
10+
11+
## Usage
12+
13+
### Run both benchmarks
14+
```bash
15+
./bin/benchmark -startheight=100 -endheight=200
16+
```
17+
18+
### Run only v1 HTTP benchmark
19+
```bash
20+
./bin/benchmark -v1 -v2=false -startheight=100 -endheight=200
21+
```
22+
23+
### Run only v2 gRPC benchmark
24+
```bash
25+
./bin/benchmark -v1=false -v2 -startheight=100 -endheight=200
26+
```
27+
28+
### Using Makefile
29+
```bash
30+
# Run both
31+
make benchmark ARGS="-startheight=100 -endheight=200"
32+
33+
# Run only v1
34+
make benchmark-v1 ARGS="-startheight=100 -endheight=200"
35+
36+
# Run only v2
37+
make benchmark-v2 ARGS="-startheight=100 -endheight=200"
38+
```
39+
40+
## Command Line Flags
41+
42+
- `-startheight`: Start block height (default: 1)
43+
- `-endheight`: End block height (default: 10)
44+
- `-http`: HTTP API base URL (default: "http://127.0.0.1:8000")
45+
- `-grpc`: gRPC server host:port (default: "127.0.0.1:50051")
46+
- `-v1`: Run v1 HTTP benchmark (default: true)
47+
- `-v2`: Run v2 gRPC benchmark (default: true)
48+
49+
## What it measures
50+
51+
The benchmark fetches block data (tweaks, filters) for each block height in the range and measures:
52+
53+
- Total time to fetch all blocks
54+
- Blocks processed per second
55+
- Individual block fetch times
56+
57+
## Expected Results
58+
59+
- **v1 (HTTP)**: Makes individual HTTP requests for each block, good for small ranges
60+
- **v2 (gRPC)**: Uses streaming to fetch all blocks in one connection, better for large ranges
61+
62+
The gRPC streaming approach should show better performance for larger block ranges due to reduced connection overhead and better batching.

cmd/benchmark/main.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
6+
"github.com/rs/zerolog"
7+
"github.com/setavenger/blindbit-lib/logging"
8+
"github.com/setavenger/blindbit-oracle/internal/benchmark"
9+
)
10+
11+
func main() {
12+
var (
13+
startHeight = flag.Uint64("startheight", 1, "Start block height")
14+
endHeight = flag.Uint64("endheight", 10, "End block height")
15+
httpURL = flag.String("http", "http://127.0.0.1:8000", "HTTP API base URL")
16+
grpcHost = flag.String("grpc", "127.0.0.1:50051", "gRPC server host:port")
17+
runV1 = flag.Bool("v1", true, "Run v1 HTTP benchmark")
18+
runV2 = flag.Bool("v2", true, "Run v2 gRPC benchmark")
19+
)
20+
flag.Parse()
21+
22+
// Setup logging
23+
logging.SetLogLevel(zerolog.InfoLevel)
24+
25+
logging.L.Info().
26+
Uint64("start_height", *startHeight).
27+
Uint64("end_height", *endHeight).
28+
Msg("Starting benchmark")
29+
30+
if *runV1 {
31+
logging.L.Info().Msg("=== Running V1 HTTP Benchmark ===")
32+
benchmark.BenchmarkV1(*startHeight, *endHeight, *httpURL)
33+
}
34+
35+
if *runV2 {
36+
logging.L.Info().Msg("=== Running V2 gRPC Streaming Benchmark ===")
37+
benchmark.BenchmarkV2(*startHeight, *endHeight, *grpcHost)
38+
}
39+
40+
logging.L.Info().Msg("Benchmark completed")
41+
}

internal/benchmark/benchmark_v1.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package benchmark
2+
3+
import (
4+
"sync"
5+
"time"
6+
7+
"github.com/setavenger/blindbit-lib/logging"
8+
"github.com/setavenger/blindbit-lib/networking"
9+
)
10+
11+
// BenchmarkV1 runs the v1 HTTP API benchmark using the existing ClientBlindBit
12+
func BenchmarkV1(startHeight, endHeight uint64, baseURL string) {
13+
logging.L.Info().Msgf("Starting v1 HTTP benchmark from height %d to %d", startHeight, endHeight)
14+
15+
// Create client using existing networking code
16+
client := &networking.ClientBlindBit{BaseURL: baseURL}
17+
18+
startTime := time.Now()
19+
20+
// Fetch data for each height
21+
for height := startHeight; height <= endHeight; height++ {
22+
blockData, err := fetchBlockDataV1(height, client)
23+
if err != nil {
24+
logging.L.Err(err).Uint64("height", height).Msg("failed to fetch block data")
25+
continue
26+
}
27+
28+
logging.L.Debug().Uint64("height", height).Msg("fetched block data")
29+
_ = blockData // Use blockData to avoid compiler warning
30+
}
31+
32+
duration := time.Since(startTime)
33+
blocksProcessed := endHeight - startHeight + 1
34+
35+
logging.L.Info().
36+
Uint64("start_height", startHeight).
37+
Uint64("end_height", endHeight).
38+
Uint64("blocks_processed", blocksProcessed).
39+
Dur("total_duration", duration).
40+
Float64("blocks_per_second", float64(blocksProcessed)/duration.Seconds()).
41+
Msg("v1 HTTP benchmark completed")
42+
}
43+
44+
// fetchBlockDataV1 fetches block data for a single height using existing ClientBlindBit
45+
func fetchBlockDataV1(height uint64, client *networking.ClientBlindBit) (*BlockDataV1, error) {
46+
var wg sync.WaitGroup
47+
wg.Add(3)
48+
49+
errChan := make(chan error, 3)
50+
51+
var filterNew, filterSpent *networking.Filter
52+
var tweaks [][33]byte
53+
54+
// Fetch new UTXOs filter
55+
go func() {
56+
defer wg.Done()
57+
var err error
58+
filterNew, err = client.GetFilter(height, networking.NewUTXOFilterType)
59+
if err != nil {
60+
logging.L.Err(err).Msg("failed to get new utxos filter")
61+
errChan <- err
62+
}
63+
}()
64+
65+
// Fetch spent outpoints filter
66+
go func() {
67+
defer wg.Done()
68+
var err error
69+
filterSpent, err = client.GetFilter(height, networking.SpentOutpointsFilterType)
70+
if err != nil {
71+
logging.L.Err(err).Msg("failed to get spent outpoints filter")
72+
errChan <- err
73+
}
74+
}()
75+
76+
// Fetch tweaks
77+
go func() {
78+
defer wg.Done()
79+
var err error
80+
tweaks, err = client.GetTweaks(height, 0) // 0 = no dust limit
81+
if err != nil {
82+
logging.L.Err(err).Msg("failed to pull tweaks")
83+
errChan <- err
84+
}
85+
}()
86+
87+
wg.Wait()
88+
89+
select {
90+
case err := <-errChan:
91+
return nil, err
92+
default:
93+
// No errors
94+
}
95+
96+
return &BlockDataV1{
97+
Height: height,
98+
FilterNew: filterNew,
99+
FilterSpent: filterSpent,
100+
Tweaks: tweaks,
101+
}, nil
102+
}
103+
104+
// BlockDataV1 represents the block data structure for v1
105+
type BlockDataV1 struct {
106+
Height uint64
107+
FilterNew *networking.Filter
108+
FilterSpent *networking.Filter
109+
Tweaks [][33]byte
110+
}

internal/benchmark/benchmark_v2.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package benchmark
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/setavenger/blindbit-lib/logging"
8+
"github.com/setavenger/blindbit-lib/proto/pb"
9+
"google.golang.org/grpc"
10+
"google.golang.org/grpc/credentials/insecure"
11+
)
12+
13+
// BenchmarkV2 runs the v2 gRPC streaming benchmark
14+
func BenchmarkV2(startHeight, endHeight uint64, grpcHost string) {
15+
logging.L.Info().Msgf("Starting v2 gRPC streaming benchmark from height %d to %d", startHeight, endHeight)
16+
17+
// Connect to gRPC server
18+
conn, err := grpc.Dial(grpcHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
19+
if err != nil {
20+
logging.L.Err(err).Msg("failed to connect to gRPC server")
21+
return
22+
}
23+
defer conn.Close()
24+
25+
client := pb.NewOracleServiceClient(conn)
26+
27+
startTime := time.Now()
28+
29+
// Use streaming API to fetch all blocks at once
30+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
31+
defer cancel()
32+
33+
req := &pb.RangedBlockHeightRequest{
34+
Start: startHeight,
35+
End: endHeight,
36+
}
37+
38+
stream, err := client.StreamBlockBatchSlim(ctx, req)
39+
if err != nil {
40+
logging.L.Err(err).Msg("failed to start streaming")
41+
return
42+
}
43+
44+
blocksProcessed := uint64(0)
45+
46+
for {
47+
batch, err := stream.Recv()
48+
if err != nil {
49+
if err.Error() == "EOF" {
50+
break
51+
}
52+
logging.L.Err(err).Msg("failed to receive batch")
53+
break
54+
}
55+
56+
blocksProcessed++
57+
logging.L.Debug().Uint64("height", uint64(batch.BlockIdentifier.BlockHeight)).Msg("received block batch")
58+
59+
// Use batch to avoid compiler warning
60+
_ = batch
61+
}
62+
63+
duration := time.Since(startTime)
64+
65+
logging.L.Info().
66+
Uint64("start_height", startHeight).
67+
Uint64("end_height", endHeight).
68+
Uint64("blocks_processed", blocksProcessed).
69+
Dur("total_duration", duration).
70+
Float64("blocks_per_second", float64(blocksProcessed)/duration.Seconds()).
71+
Msg("v2 gRPC streaming benchmark completed")
72+
}

0 commit comments

Comments
 (0)