github.com/MetalBlockchain/metalgo@v1.11.9/database/benchmark_database.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package database
     5  
     6  import (
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/MetalBlockchain/metalgo/utils/units"
    13  )
    14  
    15  var (
    16  	// Benchmarks is a list of all database benchmarks
    17  	Benchmarks = map[string]func(b *testing.B, db Database, keys, values [][]byte){
    18  		"Get":            BenchmarkGet,
    19  		"Put":            BenchmarkPut,
    20  		"Delete":         BenchmarkDelete,
    21  		"BatchPut":       BenchmarkBatchPut,
    22  		"BatchDelete":    BenchmarkBatchDelete,
    23  		"BatchWrite":     BenchmarkBatchWrite,
    24  		"ParallelGet":    BenchmarkParallelGet,
    25  		"ParallelPut":    BenchmarkParallelPut,
    26  		"ParallelDelete": BenchmarkParallelDelete,
    27  	}
    28  	// BenchmarkSizes to use with each benchmark
    29  	BenchmarkSizes = [][]int{
    30  		// count, keySize, valueSize
    31  		{1024, 32, 32},
    32  		{1024, 256, 256},
    33  		{1024, 2 * units.KiB, 2 * units.KiB},
    34  	}
    35  )
    36  
    37  // Writes size data into the db in order to setup reads in subsequent tests.
    38  func SetupBenchmark(b *testing.B, count int, keySize, valueSize int) ([][]byte, [][]byte) {
    39  	require := require.New(b)
    40  
    41  	b.Helper()
    42  
    43  	keys := make([][]byte, count)
    44  	values := make([][]byte, count)
    45  	for i := 0; i < count; i++ {
    46  		keyBytes := make([]byte, keySize)
    47  		valueBytes := make([]byte, valueSize)
    48  		_, err := rand.Read(keyBytes) // #nosec G404
    49  		require.NoError(err)
    50  		_, err = rand.Read(valueBytes) // #nosec G404
    51  		require.NoError(err)
    52  		keys[i], values[i] = keyBytes, valueBytes
    53  	}
    54  	return keys, values
    55  }
    56  
    57  // BenchmarkGet measures the time it takes to get an operation from a database.
    58  func BenchmarkGet(b *testing.B, db Database, keys, values [][]byte) {
    59  	require.NotEmpty(b, keys)
    60  	count := len(keys)
    61  
    62  	require := require.New(b)
    63  
    64  	for i, key := range keys {
    65  		value := values[i]
    66  		require.NoError(db.Put(key, value))
    67  	}
    68  
    69  	b.ResetTimer()
    70  
    71  	// Reads b.N values from the db
    72  	for i := 0; i < b.N; i++ {
    73  		_, err := db.Get(keys[i%count])
    74  		require.NoError(err)
    75  	}
    76  }
    77  
    78  // BenchmarkPut measures the time it takes to write an operation to a database.
    79  func BenchmarkPut(b *testing.B, db Database, keys, values [][]byte) {
    80  	require.NotEmpty(b, keys)
    81  	count := len(keys)
    82  
    83  	// Writes b.N values to the db
    84  	for i := 0; i < b.N; i++ {
    85  		require.NoError(b, db.Put(keys[i%count], values[i%count]))
    86  	}
    87  }
    88  
    89  // BenchmarkDelete measures the time it takes to delete a (k, v) from a database.
    90  func BenchmarkDelete(b *testing.B, db Database, keys, values [][]byte) {
    91  	require.NotEmpty(b, keys)
    92  	count := len(keys)
    93  
    94  	require := require.New(b)
    95  
    96  	// Writes random values of size _size_ to the database
    97  	for i, key := range keys {
    98  		value := values[i]
    99  		require.NoError(db.Put(key, value))
   100  	}
   101  
   102  	b.ResetTimer()
   103  
   104  	// Deletes b.N values from the db
   105  	for i := 0; i < b.N; i++ {
   106  		require.NoError(db.Delete(keys[i%count]))
   107  	}
   108  }
   109  
   110  // BenchmarkBatchPut measures the time it takes to batch put.
   111  func BenchmarkBatchPut(b *testing.B, db Database, keys, values [][]byte) {
   112  	require.NotEmpty(b, keys)
   113  	count := len(keys)
   114  
   115  	batch := db.NewBatch()
   116  	for i := 0; i < b.N; i++ {
   117  		require.NoError(b, batch.Put(keys[i%count], values[i%count]))
   118  	}
   119  }
   120  
   121  // BenchmarkBatchDelete measures the time it takes to batch delete.
   122  func BenchmarkBatchDelete(b *testing.B, db Database, keys, _ [][]byte) {
   123  	require.NotEmpty(b, keys)
   124  	count := len(keys)
   125  
   126  	batch := db.NewBatch()
   127  	for i := 0; i < b.N; i++ {
   128  		require.NoError(b, batch.Delete(keys[i%count]))
   129  	}
   130  }
   131  
   132  // BenchmarkBatchWrite measures the time it takes to batch write.
   133  func BenchmarkBatchWrite(b *testing.B, db Database, keys, values [][]byte) {
   134  	require.NotEmpty(b, keys)
   135  
   136  	require := require.New(b)
   137  
   138  	batch := db.NewBatch()
   139  	for i, key := range keys {
   140  		value := values[i]
   141  		require.NoError(batch.Put(key, value))
   142  	}
   143  
   144  	b.ResetTimer()
   145  
   146  	for i := 0; i < b.N; i++ {
   147  		require.NoError(batch.Write())
   148  	}
   149  }
   150  
   151  // BenchmarkParallelGet measures the time it takes to read in parallel.
   152  func BenchmarkParallelGet(b *testing.B, db Database, keys, values [][]byte) {
   153  	require.NotEmpty(b, keys)
   154  	count := len(keys)
   155  
   156  	require := require.New(b)
   157  
   158  	for i, key := range keys {
   159  		value := values[i]
   160  		require.NoError(db.Put(key, value))
   161  	}
   162  
   163  	b.ResetTimer()
   164  
   165  	b.RunParallel(func(pb *testing.PB) {
   166  		for i := 0; pb.Next(); i++ {
   167  			_, err := db.Get(keys[i%count])
   168  			require.NoError(err)
   169  		}
   170  	})
   171  }
   172  
   173  // BenchmarkParallelPut measures the time it takes to write to the db in parallel.
   174  func BenchmarkParallelPut(b *testing.B, db Database, keys, values [][]byte) {
   175  	require.NotEmpty(b, keys)
   176  	count := len(keys)
   177  
   178  	b.RunParallel(func(pb *testing.PB) {
   179  		// Write N values to the db
   180  		for i := 0; pb.Next(); i++ {
   181  			require.NoError(b, db.Put(keys[i%count], values[i%count]))
   182  		}
   183  	})
   184  }
   185  
   186  // BenchmarkParallelDelete measures the time it takes to delete a (k, v) from the db.
   187  func BenchmarkParallelDelete(b *testing.B, db Database, keys, values [][]byte) {
   188  	require.NotEmpty(b, keys)
   189  	count := len(keys)
   190  
   191  	require := require.New(b)
   192  	for i, key := range keys {
   193  		value := values[i]
   194  		require.NoError(db.Put(key, value))
   195  	}
   196  	b.ResetTimer()
   197  
   198  	b.RunParallel(func(pb *testing.PB) {
   199  		// Deletes b.N values from the db
   200  		for i := 0; pb.Next(); i++ {
   201  			require.NoError(db.Delete(keys[i%count]))
   202  		}
   203  	})
   204  }