diff options
Diffstat (limited to 'benchmarks')
| -rw-r--r-- | benchmarks/BENCHMARKS.md | 130 | ||||
| -rw-r--r-- | benchmarks/BENCHMARK_SUMMARY.md | 154 | ||||
| -rw-r--r-- | benchmarks/benchmark_results.txt | 32 | ||||
| -rwxr-xr-x | benchmarks/run_benchmarks.sh | 35 |
4 files changed, 351 insertions, 0 deletions
diff --git a/benchmarks/BENCHMARKS.md b/benchmarks/BENCHMARKS.md new file mode 100644 index 0000000..1a671c1 --- /dev/null +++ b/benchmarks/BENCHMARKS.md | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | # Nostr Library Benchmarks | ||
| 2 | |||
| 3 | This directory contains comprehensive benchmarks comparing three popular Go Nostr libraries: | ||
| 4 | |||
| 5 | - **NWIO** (`code.northwest.io/nostr`) - This library | ||
| 6 | - **NBD** (`github.com/nbd-wtf/go-nostr`) - Popular community library | ||
| 7 | - **Fiat** (`fiatjaf.com/nostr`) - Original implementation by Fiatjaf | ||
| 8 | |||
| 9 | ## Benchmark Categories | ||
| 10 | |||
| 11 | ### Event Operations | ||
| 12 | - **Unmarshal**: Parsing JSON into Event struct | ||
| 13 | - **Marshal**: Serializing Event struct to JSON | ||
| 14 | - **Serialize**: Canonical serialization for ID computation | ||
| 15 | - **ComputeID**: Computing event ID hash | ||
| 16 | - **Sign**: Signing events with private key | ||
| 17 | - **Verify**: Verifying event signatures | ||
| 18 | |||
| 19 | ### Key Operations | ||
| 20 | - **GenerateKey**: Generating new private keys | ||
| 21 | |||
| 22 | ### Filter Operations | ||
| 23 | - **FilterMatch**: Simple filter matching (kind, author) | ||
| 24 | - **FilterMatchComplex**: Complex filter matching (with tags, prefix matching) | ||
| 25 | |||
| 26 | ## Running Benchmarks | ||
| 27 | |||
| 28 | **Important**: The comparison benchmarks are in a separate module (`benchmarks/comparison/`) to avoid polluting the main module dependencies with competitor libraries. | ||
| 29 | |||
| 30 | ### Quick Start | ||
| 31 | |||
| 32 | Run all comparison benchmarks: | ||
| 33 | ```bash | ||
| 34 | cd benchmarks/comparison | ||
| 35 | go test -bench=. -benchmem -benchtime=1s | ||
| 36 | ``` | ||
| 37 | |||
| 38 | ### Specific Benchmark Groups | ||
| 39 | |||
| 40 | Event unmarshaling: | ||
| 41 | ```bash | ||
| 42 | cd benchmarks/comparison | ||
| 43 | go test -bench=BenchmarkEventUnmarshal -benchmem | ||
| 44 | ``` | ||
| 45 | |||
| 46 | Event signing: | ||
| 47 | ```bash | ||
| 48 | cd benchmarks/comparison | ||
| 49 | go test -bench=BenchmarkEventSign -benchmem | ||
| 50 | ``` | ||
| 51 | |||
| 52 | Event verification: | ||
| 53 | ```bash | ||
| 54 | cd benchmarks/comparison | ||
| 55 | go test -bench=BenchmarkEventVerify -benchmem | ||
| 56 | ``` | ||
| 57 | |||
| 58 | Filter matching: | ||
| 59 | ```bash | ||
| 60 | cd benchmarks/comparison | ||
| 61 | go test -bench=BenchmarkFilterMatch -benchmem | ||
| 62 | ``` | ||
| 63 | |||
| 64 | ### Compare Single Library | ||
| 65 | |||
| 66 | NWIO only: | ||
| 67 | ```bash | ||
| 68 | cd benchmarks/comparison | ||
| 69 | go test -bench='.*_NWIO' -benchmem | ||
| 70 | ``` | ||
| 71 | |||
| 72 | NBD only: | ||
| 73 | ```bash | ||
| 74 | cd benchmarks/comparison | ||
| 75 | go test -bench='.*_NBD' -benchmem | ||
| 76 | ``` | ||
| 77 | |||
| 78 | Fiat only: | ||
| 79 | ```bash | ||
| 80 | cd benchmarks/comparison | ||
| 81 | go test -bench='.*_Fiat' -benchmem | ||
| 82 | ``` | ||
| 83 | |||
| 84 | ## Analyzing Results | ||
| 85 | |||
| 86 | Use `benchstat` for statistical analysis: | ||
| 87 | |||
| 88 | ```bash | ||
| 89 | # Install benchstat | ||
| 90 | go install golang.org/x/perf/cmd/benchstat@latest | ||
| 91 | |||
| 92 | # Run benchmarks multiple times and compare | ||
| 93 | cd benchmarks/comparison | ||
| 94 | go test -bench=. -benchmem -count=10 > results.txt | ||
| 95 | benchstat results.txt | ||
| 96 | ``` | ||
| 97 | |||
| 98 | Compare two specific libraries: | ||
| 99 | ```bash | ||
| 100 | cd benchmarks/comparison | ||
| 101 | go test -bench='.*_NWIO' -benchmem -count=10 > nwio.txt | ||
| 102 | go test -bench='.*_NBD' -benchmem -count=10 > nbd.txt | ||
| 103 | benchstat nwio.txt nbd.txt | ||
| 104 | ``` | ||
| 105 | |||
| 106 | ## Understanding the Output | ||
| 107 | |||
| 108 | Example output: | ||
| 109 | ``` | ||
| 110 | BenchmarkEventSign_NWIO-24 50000 35421 ns/op 1024 B/op 12 allocs/op | ||
| 111 | ``` | ||
| 112 | |||
| 113 | - `50000`: Number of iterations | ||
| 114 | - `35421 ns/op`: Nanoseconds per operation (lower is better) | ||
| 115 | - `1024 B/op`: Bytes allocated per operation (lower is better) | ||
| 116 | - `12 allocs/op`: Memory allocations per operation (lower is better) | ||
| 117 | |||
| 118 | ## Performance Tips | ||
| 119 | |||
| 120 | 1. **Event Unmarshaling**: Critical for relay implementations | ||
| 121 | 2. **Event Signing**: Important for client implementations | ||
| 122 | 3. **Event Verification**: Critical for all implementations | ||
| 123 | 4. **Filter Matching**: Important for relay implementations with many subscriptions | ||
| 124 | |||
| 125 | ## Notes | ||
| 126 | |||
| 127 | - All benchmarks use realistic event data | ||
| 128 | - Benchmarks run with default Go test timeout | ||
| 129 | - Results may vary based on hardware and system load | ||
| 130 | - Use `-benchtime=5s` for more stable results on noisy systems | ||
diff --git a/benchmarks/BENCHMARK_SUMMARY.md b/benchmarks/BENCHMARK_SUMMARY.md new file mode 100644 index 0000000..2e087a3 --- /dev/null +++ b/benchmarks/BENCHMARK_SUMMARY.md | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | # Benchmark Results Summary | ||
| 2 | |||
| 3 | Comparison of three Go Nostr libraries: **NWIO** (code.northwest.io/nostr), **NBD** (github.com/nbd-wtf/go-nostr), and **Fiat** (fiatjaf.com/nostr) | ||
| 4 | |||
| 5 | ## Quick Performance Overview | ||
| 6 | |||
| 7 | ### 🏆 Winners by Category | ||
| 8 | |||
| 9 | | Operation | Winner | Performance | | ||
| 10 | |-----------|--------|-------------| | ||
| 11 | | **Event Unmarshal** | NWIO/Fiat | ~2.5 µs (tied) | | ||
| 12 | | **Event Marshal** | NWIO | 1.79 µs (fastest, least memory) | | ||
| 13 | | **Event Serialize** | NBD | 129 ns (3x faster than NWIO) | | ||
| 14 | | **Compute ID** | Fiat | 276 ns (2x faster than NWIO) | | ||
| 15 | | **Generate Key** | NBD | 470 ns (80x faster!) | | ||
| 16 | | **Event Sign** | NBD/Fiat | ~59 µs (2x faster than NWIO) | | ||
| 17 | | **Event Verify** | NWIO | 99.7 µs (slightly faster) | | ||
| 18 | | **Filter Match** | NWIO | 7.1 ns (2x faster than Fiat) | | ||
| 19 | | **Filter Complex** | NWIO | 30.9 ns (fastest) | | ||
| 20 | |||
| 21 | ## Detailed Results | ||
| 22 | |||
| 23 | ### Event Unmarshaling (JSON → Event) | ||
| 24 | ``` | ||
| 25 | NWIO: 2,541 ns/op 888 B/op 17 allocs/op ⭐ FASTEST, LOW MEMORY | ||
| 26 | NBD: 2,832 ns/op 944 B/op 13 allocs/op | ||
| 27 | Fiat: 2,545 ns/op 752 B/op 10 allocs/op ⭐ LEAST MEMORY | ||
| 28 | ``` | ||
| 29 | **Analysis**: All three are very competitive. NWIO and Fiat are effectively tied. Fiat uses least memory. | ||
| 30 | |||
| 31 | ### Event Marshaling (Event → JSON) | ||
| 32 | ``` | ||
| 33 | NWIO: 1,790 ns/op 1,010 B/op 3 allocs/op ⭐ FASTEST, LEAST ALLOCS | ||
| 34 | NBD: 1,819 ns/op 1,500 B/op 6 allocs/op | ||
| 35 | Fiat: 1,971 ns/op 2,254 B/op 13 allocs/op | ||
| 36 | ``` | ||
| 37 | **Analysis**: NWIO is fastest with minimal allocations. Significant memory advantage over competitors. | ||
| 38 | |||
| 39 | ### Event Serialization (for ID computation) | ||
| 40 | ``` | ||
| 41 | NWIO: 391 ns/op 360 B/op 7 allocs/op | ||
| 42 | NBD: 129 ns/op 208 B/op 2 allocs/op ⭐ FASTEST, 3x faster | ||
| 43 | Fiat: 161 ns/op 400 B/op 3 allocs/op | ||
| 44 | ``` | ||
| 45 | **Analysis**: NBD dominates here with optimized serialization. NWIO has room for improvement. | ||
| 46 | |||
| 47 | ### Event ID Computation | ||
| 48 | ``` | ||
| 49 | NWIO: 608 ns/op 488 B/op 9 allocs/op | ||
| 50 | NBD: 302 ns/op 336 B/op 4 allocs/op | ||
| 51 | Fiat: 276 ns/op 400 B/op 3 allocs/op ⭐ FASTEST | ||
| 52 | ``` | ||
| 53 | **Analysis**: NBD and Fiat are 2x faster. NWIO should optimize ID computation path. | ||
| 54 | |||
| 55 | ### Key Generation | ||
| 56 | ``` | ||
| 57 | NWIO: 37,689 ns/op 208 B/op 4 allocs/op | ||
| 58 | NBD: 470 ns/op 369 B/op 8 allocs/op ⭐ FASTEST, 80x faster! | ||
| 59 | Fiat: 25,375 ns/op 272 B/op 5 allocs/op | ||
| 60 | ``` | ||
| 61 | **Analysis**: ⚠️ NWIO is significantly slower. NBD appears to use a different key generation strategy. This is the biggest performance gap. | ||
| 62 | |||
| 63 | ### Event Signing | ||
| 64 | ``` | ||
| 65 | NWIO: 129,854 ns/op 2,363 B/op 42 allocs/op | ||
| 66 | NBD: 59,069 ns/op 2,112 B/op 35 allocs/op ⭐ TIED FASTEST | ||
| 67 | Fiat: 58,572 ns/op 1,760 B/op 29 allocs/op ⭐ LEAST MEMORY | ||
| 68 | ``` | ||
| 69 | **Analysis**: NBD and Fiat are 2x faster. NWIO has more allocations in signing path. | ||
| 70 | |||
| 71 | ### Event Verification | ||
| 72 | ``` | ||
| 73 | NWIO: 99,744 ns/op 953 B/op 19 allocs/op ⭐ FASTEST | ||
| 74 | NBD: 105,995 ns/op 624 B/op 11 allocs/op ⭐ LEAST MEMORY | ||
| 75 | Fiat: 103,744 ns/op 640 B/op 9 allocs/op | ||
| 76 | ``` | ||
| 77 | **Analysis**: NWIO is slightly faster (6% faster than others). Very competitive across all three. | ||
| 78 | |||
| 79 | ### Filter Matching (Simple) | ||
| 80 | ``` | ||
| 81 | NWIO: 7.1 ns/op 0 B/op 0 allocs/op ⭐ FASTEST, 2x faster | ||
| 82 | NBD: 10.8 ns/op 0 B/op 0 allocs/op | ||
| 83 | Fiat: 16.4 ns/op 0 B/op 0 allocs/op | ||
| 84 | ``` | ||
| 85 | **Analysis**: NWIO excels at filter matching! Zero allocations across all libraries. | ||
| 86 | |||
| 87 | ### Filter Matching (Complex with Tags) | ||
| 88 | ``` | ||
| 89 | NWIO: 30.9 ns/op 0 B/op 0 allocs/op ⭐ FASTEST | ||
| 90 | NBD: 33.4 ns/op 0 B/op 0 allocs/op | ||
| 91 | Fiat: 42.6 ns/op 0 B/op 0 allocs/op | ||
| 92 | ``` | ||
| 93 | **Analysis**: NWIO maintains lead in complex filters. Important for relay implementations. | ||
| 94 | |||
| 95 | ## Optimization Opportunities for NWIO | ||
| 96 | |||
| 97 | ### High Priority 🔴 | ||
| 98 | 1. **Key Generation** - 80x slower than NBD | ||
| 99 | - Current: 37.7 µs | ||
| 100 | - Target: ~500 ns (similar to NBD) | ||
| 101 | - Impact: Critical for client applications | ||
| 102 | |||
| 103 | 2. **Event Signing** - 2x slower than competitors | ||
| 104 | - Current: 130 µs | ||
| 105 | - Target: ~60 µs (match NBD/Fiat) | ||
| 106 | - Impact: High for client applications | ||
| 107 | |||
| 108 | ### Medium Priority 🟡 | ||
| 109 | 3. **Event Serialization** - 3x slower than NBD | ||
| 110 | - Current: 391 ns | ||
| 111 | - Target: ~130 ns (match NBD) | ||
| 112 | - Impact: Used in ID computation | ||
| 113 | |||
| 114 | 4. **ID Computation** - 2x slower than competitors | ||
| 115 | - Current: 608 ns | ||
| 116 | - Target: ~280 ns (match Fiat) | ||
| 117 | - Impact: Affects every event processing | ||
| 118 | |||
| 119 | ## Current Strengths of NWIO ✅ | ||
| 120 | |||
| 121 | 1. **Filter Matching** - 2x faster than Fiat, fastest overall | ||
| 122 | 2. **Event Marshaling** - Fastest with minimal allocations | ||
| 123 | 3. **Event Verification** - Slightly faster than competitors | ||
| 124 | 4. **Memory Efficiency** - Competitive or better in most operations | ||
| 125 | |||
| 126 | ## Recommendations | ||
| 127 | |||
| 128 | ### For Relay Implementations | ||
| 129 | - **NWIO excels**: Best filter matching performance | ||
| 130 | - All three are competitive for event parsing/verification | ||
| 131 | |||
| 132 | ### For Client Implementations | ||
| 133 | - **NBD/Fiat preferred**: Much faster key generation and signing | ||
| 134 | - NWIO needs optimization in crypto operations | ||
| 135 | |||
| 136 | ### Overall Assessment | ||
| 137 | - **NWIO**: Best for relay/filter-heavy workloads | ||
| 138 | - **NBD**: Most balanced, excellent crypto performance | ||
| 139 | - **Fiat**: Good all-around, lowest memory in some operations | ||
| 140 | |||
| 141 | ## Running Your Own Benchmarks | ||
| 142 | |||
| 143 | ```bash | ||
| 144 | # Run all benchmarks | ||
| 145 | ./run_benchmarks.sh | ||
| 146 | |||
| 147 | # Compare specific operations | ||
| 148 | go test -bench=BenchmarkEventSign -benchmem comparison_bench_test.go | ||
| 149 | go test -bench=BenchmarkFilterMatch -benchmem comparison_bench_test.go | ||
| 150 | |||
| 151 | # Statistical analysis with benchstat | ||
| 152 | go test -bench=. -count=10 comparison_bench_test.go > results.txt | ||
| 153 | benchstat results.txt | ||
| 154 | ``` | ||
diff --git a/benchmarks/benchmark_results.txt b/benchmarks/benchmark_results.txt new file mode 100644 index 0000000..c3976e6 --- /dev/null +++ b/benchmarks/benchmark_results.txt | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | goos: linux | ||
| 2 | goarch: amd64 | ||
| 3 | cpu: AMD Ryzen AI 9 HX PRO 370 w/ Radeon 890M | ||
| 4 | BenchmarkEventUnmarshal_NWIO-24 498826 2541 ns/op 888 B/op 17 allocs/op | ||
| 5 | BenchmarkEventUnmarshal_NBD-24 423019 2832 ns/op 944 B/op 13 allocs/op | ||
| 6 | BenchmarkEventUnmarshal_Fiat-24 430042 2545 ns/op 752 B/op 10 allocs/op | ||
| 7 | BenchmarkEventMarshal_NWIO-24 613165 1790 ns/op 1010 B/op 3 allocs/op | ||
| 8 | BenchmarkEventMarshal_NBD-24 620986 1819 ns/op 1500 B/op 6 allocs/op | ||
| 9 | BenchmarkEventMarshal_Fiat-24 621964 1971 ns/op 2254 B/op 13 allocs/op | ||
| 10 | BenchmarkEventSerialize_NWIO-24 3059661 391.0 ns/op 360 B/op 7 allocs/op | ||
| 11 | BenchmarkEventSerialize_NBD-24 8824029 128.8 ns/op 208 B/op 2 allocs/op | ||
| 12 | BenchmarkEventSerialize_Fiat-24 6533536 160.9 ns/op 400 B/op 3 allocs/op | ||
| 13 | BenchmarkComputeID_NWIO-24 2108437 608.0 ns/op 488 B/op 9 allocs/op | ||
| 14 | BenchmarkComputeID_NBD-24 4072243 302.2 ns/op 336 B/op 4 allocs/op | ||
| 15 | BenchmarkComputeID_Fiat-24 4421660 275.9 ns/op 400 B/op 3 allocs/op | ||
| 16 | BenchmarkGenerateKey_NWIO-24 31942 37689 ns/op 208 B/op 4 allocs/op | ||
| 17 | BenchmarkGenerateKey_NBD-24 2489169 469.6 ns/op 369 B/op 8 allocs/op | ||
| 18 | BenchmarkGenerateKey_Fiat-24 45475 25375 ns/op 272 B/op 5 allocs/op | ||
| 19 | BenchmarkEventSign_NWIO-24 9072 129854 ns/op 2363 B/op 42 allocs/op | ||
| 20 | BenchmarkEventSign_NBD-24 20325 59069 ns/op 2112 B/op 35 allocs/op | ||
| 21 | BenchmarkEventSign_Fiat-24 20613 58572 ns/op 1760 B/op 29 allocs/op | ||
| 22 | BenchmarkEventVerify_NWIO-24 12009 99744 ns/op 953 B/op 19 allocs/op | ||
| 23 | BenchmarkEventVerify_NBD-24 10000 105995 ns/op 624 B/op 11 allocs/op | ||
| 24 | BenchmarkEventVerify_Fiat-24 10000 103744 ns/op 640 B/op 9 allocs/op | ||
| 25 | BenchmarkFilterMatch_NWIO-24 167376669 7.091 ns/op 0 B/op 0 allocs/op | ||
| 26 | BenchmarkFilterMatch_NBD-24 100000000 10.82 ns/op 0 B/op 0 allocs/op | ||
| 27 | BenchmarkFilterMatch_Fiat-24 71761591 16.40 ns/op 0 B/op 0 allocs/op | ||
| 28 | BenchmarkFilterMatchComplex_NWIO-24 39214178 30.88 ns/op 0 B/op 0 allocs/op | ||
| 29 | BenchmarkFilterMatchComplex_NBD-24 35580048 33.40 ns/op 0 B/op 0 allocs/op | ||
| 30 | BenchmarkFilterMatchComplex_Fiat-24 28026481 42.64 ns/op 0 B/op 0 allocs/op | ||
| 31 | PASS | ||
| 32 | ok command-line-arguments 40.651s | ||
diff --git a/benchmarks/run_benchmarks.sh b/benchmarks/run_benchmarks.sh new file mode 100755 index 0000000..87dfd17 --- /dev/null +++ b/benchmarks/run_benchmarks.sh | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | set -e | ||
| 3 | |||
| 4 | # Colors for output | ||
| 5 | GREEN='\033[0;32m' | ||
| 6 | BLUE='\033[0;34m' | ||
| 7 | YELLOW='\033[1;33m' | ||
| 8 | NC='\033[0m' # No Color | ||
| 9 | |||
| 10 | echo -e "${BLUE}Running Nostr Library Benchmarks${NC}" | ||
| 11 | echo -e "${BLUE}Comparing: NWIO vs NBD-WTF vs Fiatjaf${NC}" | ||
| 12 | echo "" | ||
| 13 | |||
| 14 | # First, install comparison dependencies if needed | ||
| 15 | echo -e "${YELLOW}Ensuring comparison dependencies are available...${NC}" | ||
| 16 | go get -tags=benchcmp -t ./... | ||
| 17 | echo "" | ||
| 18 | |||
| 19 | # Run all benchmarks with the benchcmp build tag | ||
| 20 | echo -e "${GREEN}Running all benchmarks...${NC}" | ||
| 21 | go test -tags=benchcmp -bench=. -benchmem -benchtime=1s -run=^$ | tee benchmark_results.txt | ||
| 22 | |||
| 23 | echo "" | ||
| 24 | echo -e "${GREEN}Results saved to benchmark_results.txt${NC}" | ||
| 25 | echo "" | ||
| 26 | echo -e "${BLUE}To run specific benchmark groups:${NC}" | ||
| 27 | echo " go test -tags=benchcmp -bench=BenchmarkEventUnmarshal -benchmem" | ||
| 28 | echo " go test -tags=benchcmp -bench=BenchmarkEventSign -benchmem" | ||
| 29 | echo " go test -tags=benchcmp -bench=BenchmarkEventVerify -benchmem" | ||
| 30 | echo " go test -tags=benchcmp -bench=BenchmarkFilterMatch -benchmem" | ||
| 31 | echo "" | ||
| 32 | echo -e "${BLUE}To compare specific libraries:${NC}" | ||
| 33 | echo " go test -tags=benchcmp -bench='.*_NWIO' -benchmem" | ||
| 34 | echo " go test -tags=benchcmp -bench='.*_NBD' -benchmem" | ||
| 35 | echo " go test -tags=benchcmp -bench='.*_Fiat' -benchmem" | ||
