github.com/iotexproject/iotex-core@v1.14.1-rc1/db/trie/mptrie/merklepatriciatrie_benchmark_test.go (about)

     1  package mptrie
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/iotexproject/iotex-core/db"
    13  	"github.com/iotexproject/iotex-core/db/batch"
    14  	"github.com/iotexproject/iotex-core/db/trie"
    15  	"github.com/iotexproject/iotex-core/testutil"
    16  )
    17  
    18  func BenchmarkTrie_Get(b *testing.B)            { benchTrieGet(b, false, false) }
    19  func BenchmarkTrie_GetWithAsync(b *testing.B)   { benchTrieGet(b, true, false) }
    20  func BenchmarkTrie_GetDB(b *testing.B)          { benchTrieGet(b, false, true) }
    21  func BenchmarkTrie_GetDBWithAsync(b *testing.B) { benchTrieGet(b, true, true) }
    22  func BenchmarkTrie_UpsertLE(b *testing.B)       { benchTrieUpsert(b, binary.LittleEndian) }
    23  func BenchmarkTrie_UpsertBE(b *testing.B)       { benchTrieUpsert(b, binary.BigEndian) }
    24  
    25  const (
    26  	_benchElemCount = 20000
    27  	_keyLength      = 32
    28  )
    29  
    30  func benchTrieGet(b *testing.B, async, withDB bool) {
    31  	var (
    32  		require = require.New(b)
    33  		opts    = []Option{KeyLengthOption(_keyLength)}
    34  		flush   func() error
    35  	)
    36  	if async {
    37  		opts = append(opts, AsyncOption())
    38  	}
    39  	if withDB {
    40  		testPath, err := testutil.PathOfTempFile(fmt.Sprintf("test-kv-store-%t.bolt", async))
    41  		require.NoError(err)
    42  		defer testutil.CleanupPath(testPath)
    43  		cfg := db.DefaultConfig
    44  		cfg.DbPath = testPath
    45  		dao := db.NewBoltDB(cfg)
    46  		flusher, err := db.NewKVStoreFlusher(dao, batch.NewCachedBatch())
    47  		require.NoError(err)
    48  		flusherKV := flusher.KVStoreWithBuffer()
    49  		flush = flusher.Flush
    50  		kvStore, err := trie.NewKVStore("test", flusherKV)
    51  		require.NoError(err)
    52  		require.NoError(kvStore.Start(context.Background()))
    53  		opts = append(opts, KVStoreOption(kvStore))
    54  	}
    55  	tr, err := New(opts...)
    56  	require.NoError(err)
    57  	require.NoError(tr.Start(context.Background()))
    58  	defer require.NoError(tr.Stop(context.Background()))
    59  
    60  	key := make([]byte, _keyLength)
    61  	for i := 0; i < _benchElemCount; i++ {
    62  		binary.LittleEndian.PutUint64(key, uint64(i))
    63  		require.NoError(tr.Upsert(key, key))
    64  	}
    65  	binary.LittleEndian.PutUint64(key, uint64(_benchElemCount)/2)
    66  	if withDB {
    67  		require.NoError(flush())
    68  	}
    69  
    70  	b.ResetTimer()
    71  	for i := 0; i < b.N; i++ {
    72  		tr.Get(key)
    73  	}
    74  	b.StopTimer()
    75  	b.ReportAllocs()
    76  }
    77  
    78  func benchTrieUpsert(b *testing.B, e binary.ByteOrder) {
    79  	var (
    80  		require = require.New(b)
    81  		opts    = []Option{KeyLengthOption(_keyLength)}
    82  	)
    83  	trie, err := New(opts...)
    84  	require.NoError(err)
    85  	require.NoError(trie.Start(context.Background()))
    86  	defer require.NoError(trie.Stop(context.Background()))
    87  	k := make([]byte, _keyLength)
    88  	b.ResetTimer()
    89  	for i := 0; i < b.N; i++ {
    90  		e.PutUint64(k, uint64(i))
    91  		trie.Upsert(k, k)
    92  	}
    93  	b.StopTimer()
    94  	b.ReportAllocs()
    95  }
    96  
    97  func BenchmarkTrie_UpsertWithAsync(b *testing.B) {
    98  	tr, _, callback := initTrie(2)
    99  	defer callback()
   100  
   101  	keys := [][]byte{}
   102  	for i := 0; i < 10; i++ {
   103  		for j := 0; j < 256; j++ {
   104  			key := []byte{byte(i), byte(j)}
   105  			keys = append(keys, key)
   106  		}
   107  	}
   108  
   109  	b.ResetTimer()
   110  	for n := 0; n < b.N; n++ {
   111  		for _, v := range keys {
   112  			tr.Upsert(v, []byte{})
   113  		}
   114  	}
   115  }
   116  
   117  func BenchmarkTrie_RootHashWithAsync(b *testing.B) {
   118  	tr, flush, callback := initTrie(2)
   119  	defer callback()
   120  
   121  	keys := [][]byte{}
   122  	for i := 0; i < 10; i++ {
   123  		for j := 0; j < 256; j++ {
   124  			key := []byte{byte(i), byte(j)}
   125  			keys = append(keys, key)
   126  		}
   127  	}
   128  	for _, v := range keys {
   129  		tr.Upsert(v, []byte{})
   130  	}
   131  	tr.RootHash()
   132  	flush()
   133  
   134  	b.ResetTimer()
   135  	for n := 0; n < b.N; n++ {
   136  		tr.Upsert(keys[rand.Intn(len(keys))], []byte{})
   137  		tr.RootHash()
   138  	}
   139  }
   140  
   141  func initTrie(keyLen int) (trie.Trie, func() error, func()) {
   142  	testPath, err := testutil.PathOfTempFile("test-kv-store.bolt")
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  	cfg := db.DefaultConfig
   147  	cfg.DbPath = testPath
   148  	dao := db.NewBoltDB(cfg)
   149  	flusher, err := db.NewKVStoreFlusher(dao, batch.NewCachedBatch())
   150  	if err != nil {
   151  		panic(err)
   152  	}
   153  	flusherKV := flusher.KVStoreWithBuffer()
   154  	kvStore, err := trie.NewKVStore("test", flusherKV)
   155  	if err != nil {
   156  		panic(err)
   157  	}
   158  	err = kvStore.Start(context.Background())
   159  	if err != nil {
   160  		panic(err)
   161  	}
   162  	opts := []Option{KeyLengthOption(keyLen), AsyncOption(), KVStoreOption(kvStore)}
   163  	tr, err := New(opts...)
   164  	if err != nil {
   165  		panic(err)
   166  	}
   167  	err = tr.Start(context.Background())
   168  	if err != nil {
   169  		panic(err)
   170  	}
   171  	return tr, flusher.Flush, func() {
   172  		tr.Stop(context.Background())
   173  		testutil.CleanupPath(testPath)
   174  	}
   175  }