github.com/onflow/atree@v0.6.0/array_bench_test.go (about)

     1  /*
     2   * Atree - Scalable Arrays and Ordered Maps
     3   *
     4   * Copyright 2021 Dapper Labs, Inc.
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package atree
    20  
    21  import (
    22  	"math/rand"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  var noop Storable
    29  
    30  func BenchmarkArrayGet100x(b *testing.B) {
    31  	benchmarks := []struct {
    32  		name             string
    33  		initialArraySize int
    34  		numberOfOps      int
    35  		long             bool
    36  	}{
    37  		{"10", 10, 100, false},
    38  		{"1000", 1000, 100, false},
    39  		{"10000", 10_000, 100, false},
    40  		{"100000", 100_000, 100, false},
    41  		{"1000000", 1_000_000, 100, false},
    42  		{"10000000", 10_000_000, 100, true},
    43  	}
    44  	for _, bm := range benchmarks {
    45  		b.Run(bm.name, func(b *testing.B) {
    46  			if bm.long && testing.Short() {
    47  				b.Skipf("Skipping %s in short mode", bm.name)
    48  			}
    49  			benchmarkArrayGet(b, bm.initialArraySize, bm.numberOfOps)
    50  		})
    51  	}
    52  }
    53  
    54  func BenchmarkArrayInsert100x(b *testing.B) {
    55  	benchmarks := []struct {
    56  		name             string
    57  		initialArraySize int
    58  		numberOfOps      int
    59  		long             bool
    60  	}{
    61  		{"10", 10, 100, false},
    62  		{"1000", 1000, 100, false},
    63  		{"10000", 10_000, 100, false},
    64  		{"100000", 100_000, 100, false},
    65  		{"1000000", 1_000_000, 100, true},
    66  		{"10000000", 10_000_000, 100, true},
    67  	}
    68  	for _, bm := range benchmarks {
    69  		b.Run(bm.name, func(b *testing.B) {
    70  			if bm.long && testing.Short() {
    71  				b.Skipf("Skipping %s in short mode", bm.name)
    72  			}
    73  			benchmarkArrayInsert(b, bm.initialArraySize, bm.numberOfOps)
    74  		})
    75  	}
    76  }
    77  
    78  func BenchmarkArrayRemove100x(b *testing.B) {
    79  	benchmarks := []struct {
    80  		name             string
    81  		initialArraySize int
    82  		numberOfOps      int
    83  		long             bool
    84  	}{
    85  		{"100", 100, 100, false},
    86  		{"1000", 1000, 100, false},
    87  		{"10000", 10_000, 100, false},
    88  		{"100000", 100_000, 100, false},
    89  		{"1000000", 1_000_000, 100, true},
    90  		{"10000000", 10_000_000, 100, true},
    91  	}
    92  	for _, bm := range benchmarks {
    93  		b.Run(bm.name, func(b *testing.B) {
    94  			if bm.long && testing.Short() {
    95  				b.Skipf("Skipping %s in short mode", bm.name)
    96  			}
    97  			benchmarkArrayRemove(b, bm.initialArraySize, bm.numberOfOps)
    98  		})
    99  	}
   100  }
   101  
   102  // BenchmarkArrayRemoveAll benchmarks removing all elements in a loop.
   103  func BenchmarkArrayRemoveAll(b *testing.B) {
   104  	benchmarks := []struct {
   105  		name             string
   106  		initialArraySize int
   107  		long             bool
   108  	}{
   109  		{"100", 100, false},
   110  		{"1000", 1000, false},
   111  		{"10000", 10_000, false},
   112  		{"100000", 100_000, false},
   113  	}
   114  	for _, bm := range benchmarks {
   115  		b.Run(bm.name, func(b *testing.B) {
   116  			if bm.long && testing.Short() {
   117  				b.Skipf("Skipping %s in short mode", bm.name)
   118  			}
   119  			benchmarkArrayRemoveAll(b, bm.initialArraySize)
   120  		})
   121  	}
   122  }
   123  
   124  // BenchmarkArrayPopIterate benchmarks removing all elements using PopIterate.
   125  func BenchmarkArrayPopIterate(b *testing.B) {
   126  	benchmarks := []struct {
   127  		name             string
   128  		initialArraySize int
   129  		long             bool
   130  	}{
   131  		{"100", 100, false},
   132  		{"1000", 1000, false},
   133  		{"10000", 10_000, false},
   134  		{"100000", 100_000, false},
   135  	}
   136  	for _, bm := range benchmarks {
   137  		b.Run(bm.name, func(b *testing.B) {
   138  			if bm.long && testing.Short() {
   139  				b.Skipf("Skipping %s in short mode", bm.name)
   140  			}
   141  			benchmarkArrayPopIterate(b, bm.initialArraySize)
   142  		})
   143  	}
   144  }
   145  
   146  func BenchmarkNewArrayFromAppend(b *testing.B) {
   147  	benchmarks := []struct {
   148  		name             string
   149  		initialArraySize int
   150  		long             bool
   151  	}{
   152  		{"100", 100, false},
   153  		{"1000", 1000, false},
   154  		{"10000", 10_000, false},
   155  		{"100000", 100_000, false},
   156  	}
   157  	for _, bm := range benchmarks {
   158  		b.Run(bm.name, func(b *testing.B) {
   159  			if bm.long && testing.Short() {
   160  				b.Skipf("Skipping %s in short mode", bm.name)
   161  			}
   162  			benchmarkNewArrayFromAppend(b, bm.initialArraySize)
   163  		})
   164  	}
   165  }
   166  
   167  func BenchmarkNewArrayFromBatchData(b *testing.B) {
   168  	benchmarks := []struct {
   169  		name             string
   170  		initialArraySize int
   171  		long             bool
   172  	}{
   173  		{"100", 100, false},
   174  		{"1000", 1000, false},
   175  		{"10000", 10_000, false},
   176  		{"100000", 100_000, false},
   177  	}
   178  	for _, bm := range benchmarks {
   179  		b.Run(bm.name, func(b *testing.B) {
   180  			if bm.long && testing.Short() {
   181  				b.Skipf("Skipping %s in short mode", bm.name)
   182  			}
   183  			benchmarkNewArrayFromBatchData(b, bm.initialArraySize)
   184  		})
   185  	}
   186  }
   187  
   188  func setupArray(b *testing.B, r *rand.Rand, storage *PersistentSlabStorage, initialArraySize int) *Array {
   189  
   190  	address := Address{1, 2, 3, 4, 5, 6, 7, 8}
   191  
   192  	typeInfo := testTypeInfo{42}
   193  
   194  	array, err := NewArray(storage, address, typeInfo)
   195  	require.NoError(b, err)
   196  
   197  	for i := 0; i < initialArraySize; i++ {
   198  		v := RandomValue(r)
   199  		err := array.Append(v)
   200  		require.NoError(b, err)
   201  	}
   202  
   203  	err = storage.Commit()
   204  	require.NoError(b, err)
   205  
   206  	arrayID := array.StorageID()
   207  
   208  	storage.DropCache()
   209  
   210  	newArray, err := NewArrayWithRootID(storage, arrayID)
   211  	require.NoError(b, err)
   212  
   213  	return newArray
   214  }
   215  
   216  func benchmarkArrayGet(b *testing.B, initialArraySize, numberOfOps int) {
   217  
   218  	b.StopTimer()
   219  
   220  	r := newRand(b)
   221  
   222  	storage := newTestPersistentStorage(b)
   223  
   224  	array := setupArray(b, r, storage, initialArraySize)
   225  
   226  	var storable Storable
   227  
   228  	b.StartTimer()
   229  
   230  	for i := 0; i < b.N; i++ {
   231  		for i := 0; i < numberOfOps; i++ {
   232  			index := r.Intn(int(array.Count()))
   233  			storable, _ = array.Get(uint64(index))
   234  		}
   235  	}
   236  
   237  	noop = storable
   238  }
   239  
   240  func benchmarkArrayInsert(b *testing.B, initialArraySize, numberOfOps int) {
   241  
   242  	b.StopTimer()
   243  
   244  	r := newRand(b)
   245  
   246  	storage := newTestPersistentStorage(b)
   247  
   248  	for i := 0; i < b.N; i++ {
   249  
   250  		b.StopTimer()
   251  
   252  		array := setupArray(b, r, storage, initialArraySize)
   253  
   254  		b.StartTimer()
   255  
   256  		for i := 0; i < numberOfOps; i++ {
   257  			index := r.Intn(int(array.Count()))
   258  			v := RandomValue(r)
   259  			_ = array.Insert(uint64(index), v)
   260  		}
   261  	}
   262  }
   263  
   264  func benchmarkArrayRemove(b *testing.B, initialArraySize, numberOfOps int) {
   265  
   266  	b.StopTimer()
   267  
   268  	r := newRand(b)
   269  
   270  	storage := newTestPersistentStorage(b)
   271  
   272  	for i := 0; i < b.N; i++ {
   273  
   274  		b.StopTimer()
   275  
   276  		array := setupArray(b, r, storage, initialArraySize)
   277  
   278  		b.StartTimer()
   279  
   280  		for i := 0; i < numberOfOps; i++ {
   281  			index := r.Intn(int(array.Count()))
   282  			_, _ = array.Remove(uint64(index))
   283  		}
   284  	}
   285  }
   286  
   287  func benchmarkArrayRemoveAll(b *testing.B, initialArraySize int) {
   288  
   289  	b.StopTimer()
   290  
   291  	r := newRand(b)
   292  
   293  	storage := newTestPersistentStorage(b)
   294  
   295  	var storable Storable
   296  
   297  	for i := 0; i < b.N; i++ {
   298  
   299  		b.StopTimer()
   300  
   301  		array := setupArray(b, r, storage, initialArraySize)
   302  
   303  		b.StartTimer()
   304  
   305  		for i := initialArraySize - 1; i >= 0; i-- {
   306  			storable, _ = array.Remove(uint64(i))
   307  		}
   308  	}
   309  
   310  	noop = storable
   311  }
   312  
   313  func benchmarkArrayPopIterate(b *testing.B, initialArraySize int) {
   314  
   315  	b.StopTimer()
   316  
   317  	r := newRand(b)
   318  
   319  	storage := newTestPersistentStorage(b)
   320  
   321  	var storable Storable
   322  
   323  	for i := 0; i < b.N; i++ {
   324  
   325  		b.StopTimer()
   326  
   327  		array := setupArray(b, r, storage, initialArraySize)
   328  
   329  		b.StartTimer()
   330  
   331  		err := array.PopIterate(func(s Storable) {
   332  			storable = s
   333  		})
   334  		if err != nil {
   335  			b.Errorf(err.Error())
   336  		}
   337  	}
   338  
   339  	noop = storable
   340  }
   341  
   342  func benchmarkNewArrayFromAppend(b *testing.B, initialArraySize int) {
   343  
   344  	b.StopTimer()
   345  
   346  	r := newRand(b)
   347  
   348  	storage := newTestPersistentStorage(b)
   349  
   350  	array := setupArray(b, r, storage, initialArraySize)
   351  
   352  	b.StartTimer()
   353  
   354  	for i := 0; i < b.N; i++ {
   355  		copied, _ := NewArray(storage, array.Address(), array.Type())
   356  
   357  		_ = array.Iterate(func(value Value) (bool, error) {
   358  			_ = copied.Append(value)
   359  			return true, nil
   360  		})
   361  
   362  		if copied.Count() != array.Count() {
   363  			b.Errorf("Copied array has %d elements, want %d", copied.Count(), array.Count())
   364  		}
   365  	}
   366  }
   367  
   368  func benchmarkNewArrayFromBatchData(b *testing.B, initialArraySize int) {
   369  
   370  	b.StopTimer()
   371  
   372  	r := newRand(b)
   373  
   374  	storage := newTestPersistentStorage(b)
   375  
   376  	array := setupArray(b, r, storage, initialArraySize)
   377  
   378  	b.StartTimer()
   379  
   380  	for i := 0; i < b.N; i++ {
   381  		iter, err := array.Iterator()
   382  		require.NoError(b, err)
   383  
   384  		copied, _ := NewArrayFromBatchData(storage, array.Address(), array.Type(), func() (Value, error) {
   385  			return iter.Next()
   386  		})
   387  
   388  		if copied.Count() != array.Count() {
   389  			b.Errorf("Copied array has %d elements, want %d", copied.Count(), array.Count())
   390  		}
   391  	}
   392  }