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  }