github.com/ethersphere/bee/v2@v2.2.0/pkg/bmt/benchmark_test.go (about) 1 // Copyright 2021 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package bmt_test 6 7 import ( 8 "fmt" 9 "testing" 10 11 "github.com/ethersphere/bee/v2/pkg/bmt" 12 "github.com/ethersphere/bee/v2/pkg/bmt/reference" 13 "github.com/ethersphere/bee/v2/pkg/swarm" 14 "github.com/ethersphere/bee/v2/pkg/util/testutil" 15 "golang.org/x/sync/errgroup" 16 ) 17 18 func BenchmarkBMT(b *testing.B) { 19 for size := 4096; size >= 128; size /= 2 { 20 b.Run(fmt.Sprintf("%v_size_%v", "SHA3", size), func(b *testing.B) { 21 benchmarkSHA3(b, size) 22 }) 23 b.Run(fmt.Sprintf("%v_size_%v", "Baseline", size), func(b *testing.B) { 24 benchmarkBMTBaseline(b, size) 25 }) 26 b.Run(fmt.Sprintf("%v_size_%v", "REF", size), func(b *testing.B) { 27 benchmarkRefHasher(b, size) 28 }) 29 b.Run(fmt.Sprintf("%v_size_%v", "BMT", size), func(b *testing.B) { 30 benchmarkBMT(b, size) 31 }) 32 } 33 } 34 35 func BenchmarkPool(b *testing.B) { 36 for _, c := range []int{1, 8, 16, 32, 64} { 37 b.Run(fmt.Sprintf("poolsize_%v", c), func(b *testing.B) { 38 benchmarkPool(b, c) 39 }) 40 } 41 } 42 43 // benchmarks simple sha3 hash on chunks 44 func benchmarkSHA3(b *testing.B, n int) { 45 b.Helper() 46 47 testData := testutil.RandBytesWithSeed(b, 4096, seed) 48 49 b.ReportAllocs() 50 b.ResetTimer() 51 for i := 0; i < b.N; i++ { 52 if _, err := bmt.Sha3hash(testData[:n]); err != nil { 53 b.Fatalf("seed %d: %v", seed, err) 54 } 55 } 56 } 57 58 // benchmarks the minimum hashing time for a balanced (for simplicity) BMT 59 // by doing count/segmentsize parallel hashings of 2*segmentsize bytes 60 // doing it on n testPoolSize each reusing the base hasher 61 // the premise is that this is the minimum computation needed for a BMT 62 // therefore this serves as a theoretical optimum for concurrent implementations 63 func benchmarkBMTBaseline(b *testing.B, n int) { 64 b.Helper() 65 66 testData := testutil.RandBytesWithSeed(b, 4096, seed) 67 68 b.ReportAllocs() 69 b.ResetTimer() 70 for i := 0; i < b.N; i++ { 71 eg := new(errgroup.Group) 72 for j := 0; j < testSegmentCount; j++ { 73 eg.Go(func() error { 74 _, err := bmt.Sha3hash(testData[:hashSize]) 75 return err 76 }) 77 } 78 if err := eg.Wait(); err != nil { 79 b.Fatalf("seed %d: %v", seed, err) 80 } 81 } 82 } 83 84 // benchmarks BMT Hasher 85 func benchmarkBMT(b *testing.B, n int) { 86 b.Helper() 87 88 testData := testutil.RandBytesWithSeed(b, 4096, seed) 89 90 pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, testPoolSize)) 91 h := pool.Get() 92 defer pool.Put(h) 93 94 b.ReportAllocs() 95 b.ResetTimer() 96 for i := 0; i < b.N; i++ { 97 if _, err := syncHash(h, testData[:n]); err != nil { 98 b.Fatalf("seed %d: %v", seed, err) 99 } 100 } 101 } 102 103 // benchmarks 100 concurrent bmt hashes with pool capacity 104 func benchmarkPool(b *testing.B, poolsize int) { 105 b.Helper() 106 107 testData := testutil.RandBytesWithSeed(b, 4096, seed) 108 109 pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, poolsize)) 110 cycles := 100 111 112 b.ReportAllocs() 113 b.ResetTimer() 114 for i := 0; i < b.N; i++ { 115 eg := new(errgroup.Group) 116 for j := 0; j < cycles; j++ { 117 eg.Go(func() error { 118 h := pool.Get() 119 defer pool.Put(h) 120 _, err := syncHash(h, testData[:h.Capacity()]) 121 return err 122 }) 123 } 124 if err := eg.Wait(); err != nil { 125 b.Fatalf("seed %d: %v", seed, err) 126 } 127 } 128 } 129 130 // benchmarks the reference hasher 131 func benchmarkRefHasher(b *testing.B, n int) { 132 b.Helper() 133 134 testData := testutil.RandBytesWithSeed(b, 4096, seed) 135 136 rbmt := reference.NewRefHasher(swarm.NewHasher(), 128) 137 138 b.ReportAllocs() 139 b.ResetTimer() 140 for i := 0; i < b.N; i++ { 141 _, err := rbmt.Hash(testData[:n]) 142 if err != nil { 143 b.Fatal(err) 144 } 145 } 146 }