github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/bench_rocksdb_test.go (about)

     1  // Copyright 2014 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package storage
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"math/rand"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/base"
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    22  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    23  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    24  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    25  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    26  )
    27  
    28  func setupMVCCRocksDB(b testing.TB, dir string) Engine {
    29  	cache := NewRocksDBCache(1 << 30 /* 1GB */)
    30  	defer cache.Release()
    31  
    32  	rocksdb, err := NewRocksDB(
    33  		RocksDBConfig{
    34  			StorageConfig: base.StorageConfig{
    35  				Settings: cluster.MakeTestingClusterSettings(),
    36  				Dir:      dir,
    37  			},
    38  		},
    39  		cache,
    40  	)
    41  	if err != nil {
    42  		b.Fatalf("could not create new rocksdb db instance at %s: %+v", dir, err)
    43  	}
    44  	return rocksdb
    45  }
    46  
    47  func setupMVCCInMemRocksDB(_ testing.TB, loc string) Engine {
    48  	return newRocksDBInMem(roachpb.Attributes{}, testCacheSize)
    49  }
    50  
    51  // Read benchmarks. All of them run with on-disk data.
    52  
    53  func BenchmarkMVCCScan_RocksDB(b *testing.B) {
    54  	if testing.Short() {
    55  		b.Skip("TODO: fix benchmark")
    56  	}
    57  
    58  	ctx := context.Background()
    59  	for _, numRows := range []int{1, 10, 100, 1000, 10000} {
    60  		b.Run(fmt.Sprintf("rows=%d", numRows), func(b *testing.B) {
    61  			for _, numVersions := range []int{1, 2, 10, 100} {
    62  				b.Run(fmt.Sprintf("versions=%d", numVersions), func(b *testing.B) {
    63  					for _, valueSize := range []int{8, 64, 512} {
    64  						b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
    65  							runMVCCScan(ctx, b, setupMVCCRocksDB, benchScanOptions{
    66  								benchDataOptions: benchDataOptions{
    67  									numVersions: numVersions,
    68  									valueBytes:  valueSize,
    69  								},
    70  								numRows: numRows,
    71  								reverse: false,
    72  							})
    73  						})
    74  					}
    75  				})
    76  			}
    77  		})
    78  	}
    79  }
    80  
    81  func BenchmarkMVCCReverseScan_RocksDB(b *testing.B) {
    82  	if testing.Short() {
    83  		b.Skip("TODO: fix benchmark")
    84  	}
    85  
    86  	ctx := context.Background()
    87  	for _, numRows := range []int{1, 10, 100, 1000, 10000} {
    88  		b.Run(fmt.Sprintf("rows=%d", numRows), func(b *testing.B) {
    89  			for _, numVersions := range []int{1, 2, 10, 100} {
    90  				b.Run(fmt.Sprintf("versions=%d", numVersions), func(b *testing.B) {
    91  					for _, valueSize := range []int{8, 64, 512} {
    92  						b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
    93  							runMVCCScan(ctx, b, setupMVCCRocksDB, benchScanOptions{
    94  								benchDataOptions: benchDataOptions{
    95  									numVersions: numVersions,
    96  									valueBytes:  valueSize,
    97  								},
    98  								numRows: numRows,
    99  								reverse: true,
   100  							})
   101  						})
   102  					}
   103  				})
   104  			}
   105  		})
   106  	}
   107  }
   108  
   109  func BenchmarkMVCCScanTransactionalData_RocksDB(b *testing.B) {
   110  	ctx := context.Background()
   111  	runMVCCScan(ctx, b, setupMVCCRocksDB, benchScanOptions{
   112  		numRows: 10000,
   113  		benchDataOptions: benchDataOptions{
   114  			numVersions:   2,
   115  			valueBytes:    8,
   116  			transactional: true,
   117  		},
   118  	})
   119  }
   120  
   121  func BenchmarkMVCCGet_RocksDB(b *testing.B) {
   122  	ctx := context.Background()
   123  	for _, numVersions := range []int{1, 10, 100} {
   124  		b.Run(fmt.Sprintf("versions=%d", numVersions), func(b *testing.B) {
   125  			for _, valueSize := range []int{8} {
   126  				b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   127  					runMVCCGet(ctx, b, setupMVCCRocksDB, benchDataOptions{
   128  						numVersions: numVersions,
   129  						valueBytes:  valueSize,
   130  					})
   131  				})
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func BenchmarkMVCCComputeStats_RocksDB(b *testing.B) {
   138  	if testing.Short() {
   139  		b.Skip("short flag")
   140  	}
   141  	ctx := context.Background()
   142  	for _, valueSize := range []int{8, 32, 256} {
   143  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   144  			runMVCCComputeStats(ctx, b, setupMVCCRocksDB, valueSize)
   145  		})
   146  	}
   147  }
   148  
   149  func BenchmarkMVCCFindSplitKey_RocksDB(b *testing.B) {
   150  	ctx := context.Background()
   151  	for _, valueSize := range []int{32} {
   152  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   153  			runMVCCFindSplitKey(ctx, b, setupMVCCRocksDB, valueSize)
   154  		})
   155  	}
   156  }
   157  
   158  func BenchmarkIterOnBatch_RocksDB(b *testing.B) {
   159  	ctx := context.Background()
   160  	for _, writes := range []int{10, 100, 1000, 10000} {
   161  		b.Run(fmt.Sprintf("writes=%d", writes), func(b *testing.B) {
   162  			benchmarkIterOnBatch(ctx, b, writes)
   163  		})
   164  	}
   165  }
   166  
   167  // BenchmarkIterOnReadOnly_RocksDB is a microbenchmark that measures the
   168  // performance of creating an iterator and seeking to a key if a read-only
   169  // ReadWriter that caches the RocksDB iterator is used
   170  func BenchmarkIterOnReadOnly_RocksDB(b *testing.B) {
   171  	for _, writes := range []int{10, 100, 1000, 10000} {
   172  		b.Run(fmt.Sprintf("writes=%d", writes), func(b *testing.B) {
   173  			benchmarkIterOnReadWriter(b, writes, Engine.NewReadOnly, true)
   174  		})
   175  	}
   176  }
   177  
   178  // BenchmarkIterOnEngine_RocksDB is a microbenchmark that measures the
   179  // performance of creating an iterator and seeking to a key without caching is
   180  // used (see BenchmarkIterOnIterCacher_RocksDB).
   181  func BenchmarkIterOnEngine_RocksDB(b *testing.B) {
   182  	for _, writes := range []int{10, 100, 1000, 10000} {
   183  		b.Run(fmt.Sprintf("writes=%d", writes), func(b *testing.B) {
   184  			benchmarkIterOnReadWriter(b, writes, func(e Engine) ReadWriter { return e }, false)
   185  		})
   186  	}
   187  }
   188  
   189  // Write benchmarks. Most of them run in-memory except for DeleteRange benchs,
   190  // which make more sense when data is present.
   191  
   192  func BenchmarkMVCCPut_RocksDB(b *testing.B) {
   193  	ctx := context.Background()
   194  	for _, valueSize := range []int{10, 100, 1000, 10000} {
   195  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   196  			runMVCCPut(ctx, b, setupMVCCInMemRocksDB, valueSize)
   197  		})
   198  	}
   199  }
   200  
   201  func BenchmarkMVCCBlindPut_RocksDB(b *testing.B) {
   202  	ctx := context.Background()
   203  	for _, valueSize := range []int{10, 100, 1000, 10000} {
   204  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   205  			runMVCCBlindPut(ctx, b, setupMVCCInMemRocksDB, valueSize)
   206  		})
   207  	}
   208  }
   209  
   210  func BenchmarkMVCCConditionalPut_RocksDB(b *testing.B) {
   211  	ctx := context.Background()
   212  	for _, createFirst := range []bool{false, true} {
   213  		prefix := "Create"
   214  		if createFirst {
   215  			prefix = "Replace"
   216  		}
   217  		b.Run(prefix, func(b *testing.B) {
   218  			for _, valueSize := range []int{10, 100, 1000, 10000} {
   219  				b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   220  					runMVCCConditionalPut(ctx, b, setupMVCCInMemRocksDB, valueSize, createFirst)
   221  				})
   222  			}
   223  		})
   224  	}
   225  }
   226  
   227  func BenchmarkMVCCBlindConditionalPut_RocksDB(b *testing.B) {
   228  	ctx := context.Background()
   229  	for _, valueSize := range []int{10, 100, 1000, 10000} {
   230  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   231  			runMVCCBlindConditionalPut(ctx, b, setupMVCCInMemRocksDB, valueSize)
   232  		})
   233  	}
   234  }
   235  
   236  func BenchmarkMVCCInitPut_RocksDB(b *testing.B) {
   237  	ctx := context.Background()
   238  	for _, valueSize := range []int{10, 100, 1000, 10000} {
   239  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   240  			runMVCCInitPut(ctx, b, setupMVCCInMemRocksDB, valueSize)
   241  		})
   242  	}
   243  }
   244  
   245  func BenchmarkMVCCBlindInitPut_RocksDB(b *testing.B) {
   246  	ctx := context.Background()
   247  	for _, valueSize := range []int{10, 100, 1000, 10000} {
   248  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   249  			runMVCCBlindInitPut(ctx, b, setupMVCCInMemRocksDB, valueSize)
   250  		})
   251  	}
   252  }
   253  
   254  func BenchmarkMVCCPutDelete_RocksDB(b *testing.B) {
   255  	ctx := context.Background()
   256  	rocksdb := setupMVCCInMemRocksDB(b, "put_delete")
   257  	defer rocksdb.Close()
   258  
   259  	r := rand.New(rand.NewSource(timeutil.Now().UnixNano()))
   260  	value := roachpb.MakeValueFromBytes(randutil.RandBytes(r, 10))
   261  	var blockNum int64
   262  
   263  	for i := 0; i < b.N; i++ {
   264  		blockID := r.Int63()
   265  		blockNum++
   266  		key := encoding.EncodeVarintAscending(nil, blockID)
   267  		key = encoding.EncodeVarintAscending(key, blockNum)
   268  
   269  		if err := MVCCPut(ctx, rocksdb, nil, key, hlc.Timestamp{}, value, nil /* txn */); err != nil {
   270  			b.Fatal(err)
   271  		}
   272  		if err := MVCCDelete(ctx, rocksdb, nil, key, hlc.Timestamp{}, nil /* txn */); err != nil {
   273  			b.Fatal(err)
   274  		}
   275  	}
   276  }
   277  
   278  func BenchmarkMVCCBatchPut_RocksDB(b *testing.B) {
   279  	ctx := context.Background()
   280  	for _, valueSize := range []int{10} {
   281  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   282  			for _, batchSize := range []int{1, 100, 10000, 100000} {
   283  				b.Run(fmt.Sprintf("batchSize=%d", batchSize), func(b *testing.B) {
   284  					runMVCCBatchPut(ctx, b, setupMVCCInMemRocksDB, valueSize, batchSize)
   285  				})
   286  			}
   287  		})
   288  	}
   289  }
   290  
   291  func BenchmarkMVCCBatchTimeSeries_RocksDB(b *testing.B) {
   292  	ctx := context.Background()
   293  	for _, batchSize := range []int{282} {
   294  		b.Run(fmt.Sprintf("batchSize=%d", batchSize), func(b *testing.B) {
   295  			runMVCCBatchTimeSeries(ctx, b, setupMVCCInMemRocksDB, batchSize)
   296  		})
   297  	}
   298  }
   299  
   300  // BenchmarkMVCCMergeTimeSeries computes performance of merging time series
   301  // data. Uses an in-memory engine.
   302  func BenchmarkMVCCMergeTimeSeries_RocksDB(b *testing.B) {
   303  	ctx := context.Background()
   304  	ts := &roachpb.InternalTimeSeriesData{
   305  		StartTimestampNanos: 0,
   306  		SampleDurationNanos: 1000,
   307  		Samples: []roachpb.InternalTimeSeriesSample{
   308  			{Offset: 0, Count: 1, Sum: 5.0},
   309  		},
   310  	}
   311  	var value roachpb.Value
   312  	if err := value.SetProto(ts); err != nil {
   313  		b.Fatal(err)
   314  	}
   315  	runMVCCMerge(ctx, b, setupMVCCInMemRocksDB, &value, 1024)
   316  }
   317  
   318  // BenchmarkMVCCGetMergedTimeSeries computes performance of reading merged
   319  // time series data using `MVCCGet()`. Uses an in-memory engine.
   320  func BenchmarkMVCCGetMergedTimeSeries_RocksDB(b *testing.B) {
   321  	if testing.Short() {
   322  		b.Skip("short flag")
   323  	}
   324  	ctx := context.Background()
   325  	for _, numKeys := range []int{1, 16, 256} {
   326  		b.Run(fmt.Sprintf("numKeys=%d", numKeys), func(b *testing.B) {
   327  			for _, mergesPerKey := range []int{1, 16, 256} {
   328  				b.Run(fmt.Sprintf("mergesPerKey=%d", mergesPerKey), func(b *testing.B) {
   329  					runMVCCGetMergedValue(ctx, b, setupMVCCInMemRocksDB, numKeys, mergesPerKey)
   330  				})
   331  			}
   332  		})
   333  	}
   334  }
   335  
   336  // DeleteRange benchmarks below (using on-disk data).
   337  
   338  func BenchmarkMVCCDeleteRange_RocksDB(b *testing.B) {
   339  	if testing.Short() {
   340  		b.Skip("short flag")
   341  	}
   342  	ctx := context.Background()
   343  	for _, valueSize := range []int{8, 32, 256} {
   344  		b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   345  			runMVCCDeleteRange(ctx, b, setupMVCCRocksDB, valueSize)
   346  		})
   347  	}
   348  }
   349  
   350  func BenchmarkClearRange_RocksDB(b *testing.B) {
   351  	if testing.Short() {
   352  		b.Skip("TODO: fix benchmark")
   353  	}
   354  	ctx := context.Background()
   355  	runClearRange(ctx, b, setupMVCCRocksDB, func(eng Engine, batch Batch, start, end MVCCKey) error {
   356  		return batch.ClearRange(start, end)
   357  	})
   358  }
   359  
   360  func BenchmarkClearIterRange_RocksDB(b *testing.B) {
   361  	ctx := context.Background()
   362  	runClearRange(ctx, b, setupMVCCRocksDB, func(eng Engine, batch Batch, start, end MVCCKey) error {
   363  		iter := eng.NewIterator(IterOptions{UpperBound: roachpb.KeyMax})
   364  		defer iter.Close()
   365  		return batch.ClearIterRange(iter, start.Key, end.Key)
   366  	})
   367  }
   368  
   369  func BenchmarkMVCCGarbageCollect_RocksDB(b *testing.B) {
   370  	if testing.Short() {
   371  		b.Skip("short flag")
   372  	}
   373  
   374  	// NB: To debug #16068, test only 128-128-15000-6.
   375  	ctx := context.Background()
   376  	for _, keySize := range []int{128} {
   377  		b.Run(fmt.Sprintf("keySize=%d", keySize), func(b *testing.B) {
   378  			for _, valSize := range []int{128} {
   379  				b.Run(fmt.Sprintf("valSize=%d", valSize), func(b *testing.B) {
   380  					for _, numKeys := range []int{1, 1024} {
   381  						b.Run(fmt.Sprintf("numKeys=%d", numKeys), func(b *testing.B) {
   382  							for _, numVersions := range []int{2, 1024} {
   383  								b.Run(fmt.Sprintf("numVersions=%d", numVersions), func(b *testing.B) {
   384  									runMVCCGarbageCollect(ctx, b, setupMVCCInMemRocksDB, benchGarbageCollectOptions{
   385  										benchDataOptions: benchDataOptions{
   386  											numKeys:     numKeys,
   387  											numVersions: numVersions,
   388  											valueBytes:  valSize,
   389  										},
   390  										keyBytes:       keySize,
   391  										deleteVersions: numVersions - 1,
   392  									})
   393  								})
   394  							}
   395  						})
   396  					}
   397  				})
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func BenchmarkBatchApplyBatchRepr_RocksDB(b *testing.B) {
   404  	if testing.Short() {
   405  		b.Skip("short flag")
   406  	}
   407  	ctx := context.Background()
   408  	for _, indexed := range []bool{false, true} {
   409  		b.Run(fmt.Sprintf("indexed=%t", indexed), func(b *testing.B) {
   410  			for _, sequential := range []bool{false, true} {
   411  				b.Run(fmt.Sprintf("seq=%t", sequential), func(b *testing.B) {
   412  					for _, valueSize := range []int{10} {
   413  						b.Run(fmt.Sprintf("valueSize=%d", valueSize), func(b *testing.B) {
   414  							for _, batchSize := range []int{10000} {
   415  								b.Run(fmt.Sprintf("batchSize=%d", batchSize), func(b *testing.B) {
   416  									runBatchApplyBatchRepr(ctx, b, setupMVCCInMemRocksDB,
   417  										indexed, sequential, valueSize, batchSize)
   418  								})
   419  							}
   420  						})
   421  					}
   422  				})
   423  			}
   424  		})
   425  	}
   426  }
   427  
   428  func BenchmarkBatchBuilderPut(b *testing.B) {
   429  	value := make([]byte, 10)
   430  	for i := range value {
   431  		value[i] = byte(i)
   432  	}
   433  	keyBuf := append(make([]byte, 0, 64), []byte("key-")...)
   434  
   435  	b.ResetTimer()
   436  
   437  	const batchSize = 1000
   438  	batch := &RocksDBBatchBuilder{}
   439  	for i := 0; i < b.N; i += batchSize {
   440  		end := i + batchSize
   441  		if end > b.N {
   442  			end = b.N
   443  		}
   444  
   445  		for j := i; j < end; j++ {
   446  			key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(j)))
   447  			ts := hlc.Timestamp{WallTime: int64(j)}
   448  			batch.Put(MVCCKey{key, ts}, value)
   449  		}
   450  		batch.Finish()
   451  	}
   452  
   453  	b.StopTimer()
   454  }