github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/prob/dyn_cuckoo_test.go (about)

     1  // Package prob implements fully features dynamic probabilistic filter.
     2  /*
     3   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package prob_test
     6  
     7  import (
     8  	"math/rand"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/NVIDIA/aistore/cmn/prob"
    13  	"github.com/NVIDIA/aistore/tools/trand"
    14  	. "github.com/onsi/ginkgo/v2"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  const (
    19  	testFilterInitSize = 100 * 1000
    20  	objNameLength      = 5
    21  )
    22  
    23  // Predefined buckets.
    24  var buckets = []string{
    25  	"test", "imagenet", "cifar", "secret", "something-t1-d1345",
    26  }
    27  
    28  func randObjName(n int) []byte {
    29  	return []byte(buckets[rand.Intn(len(buckets))] + "/" + trand.String(n))
    30  }
    31  
    32  func genKeys(keysNum int) [][]byte {
    33  	keys := make([][]byte, keysNum)
    34  	for i := range keysNum {
    35  		keys[i] = randObjName(objNameLength)
    36  	}
    37  	return keys
    38  }
    39  
    40  var _ = Describe("Filter", func() {
    41  	filter := prob.NewFilter(testFilterInitSize)
    42  
    43  	BeforeEach(func() {
    44  		filter.Reset()
    45  	})
    46  
    47  	Context("Lookup", func() {
    48  		It("should correctly lookup a key in filter", func() {
    49  			key := []byte("key")
    50  			filter.Insert(key)
    51  			Expect(filter.Lookup(key)).To(BeTrue())
    52  		})
    53  
    54  		It("should lookup the keys in filter with no more than 0.06% failure rate", func() {
    55  			keys := genKeys(testFilterInitSize * 10)
    56  			total := float64(len(keys))
    57  			for _, key := range keys {
    58  				filter.Insert(key)
    59  			}
    60  
    61  			failures := 0
    62  			for _, key := range keys {
    63  				if !filter.Lookup(key) {
    64  					failures++
    65  				}
    66  			}
    67  
    68  			Expect(float64(failures) / total * 100).To(BeNumerically("<=", 0.006*total))
    69  		})
    70  	})
    71  
    72  	Context("Delete", func() {
    73  		It("should correctly delete a key from filter", func() {
    74  			key := []byte("key")
    75  			filter.Insert(key)
    76  			Expect(filter.Lookup(key)).To(BeTrue())
    77  			filter.Delete(key)
    78  			Expect(filter.Lookup(key)).To(BeFalse())
    79  			filter.Delete(key) // try to delete already deleted key
    80  			Expect(filter.Lookup(key)).To(BeFalse())
    81  
    82  			// do it again to check if the filter wasn't broken
    83  			filter.Insert(key)
    84  			Expect(filter.Lookup(key)).To(BeTrue())
    85  			filter.Delete(key)
    86  			Expect(filter.Lookup(key)).To(BeFalse())
    87  		})
    88  	})
    89  })
    90  
    91  func BenchmarkInsert(b *testing.B) {
    92  	b.Run("preallocated", func(b *testing.B) {
    93  		keys := genKeys(b.N)
    94  		filter := prob.NewFilter(uint(b.N))
    95  
    96  		b.ResetTimer()
    97  		for n := range b.N {
    98  			filter.Insert(keys[n])
    99  		}
   100  	})
   101  
   102  	b.Run("empty", func(b *testing.B) {
   103  		keys := genKeys(b.N)
   104  		filter := prob.NewFilter(10)
   105  
   106  		b.ResetTimer()
   107  		for n := range b.N {
   108  			filter.Insert(keys[n])
   109  		}
   110  	})
   111  }
   112  
   113  func BenchmarkLookup(b *testing.B) {
   114  	b.Run("single filter", func(b *testing.B) {
   115  		keys := genKeys(b.N)
   116  		filter := prob.NewFilter(uint(b.N))
   117  		for n := range b.N {
   118  			filter.Insert(keys[n])
   119  		}
   120  
   121  		b.ResetTimer()
   122  		for n := range b.N {
   123  			filter.Lookup(keys[n])
   124  		}
   125  	})
   126  
   127  	b.Run("multiple filters", func(b *testing.B) {
   128  		keys := genKeys(b.N)
   129  		filter := prob.NewFilter(10)
   130  		for n := range b.N {
   131  			filter.Insert(keys[n])
   132  		}
   133  
   134  		b.ResetTimer()
   135  		for n := range b.N {
   136  			filter.Lookup(keys[n])
   137  		}
   138  	})
   139  }
   140  
   141  func BenchmarkDelete(b *testing.B) {
   142  	b.Run("single filter", func(b *testing.B) {
   143  		keys := genKeys(b.N)
   144  		filter := prob.NewFilter(uint(b.N))
   145  		for n := range b.N {
   146  			filter.Insert(keys[n])
   147  		}
   148  
   149  		b.ResetTimer()
   150  		for n := range b.N {
   151  			filter.Delete(keys[n])
   152  		}
   153  	})
   154  
   155  	b.Run("multiple filters", func(b *testing.B) {
   156  		keys := genKeys(b.N)
   157  		filter := prob.NewFilter(10)
   158  		for n := range b.N {
   159  			filter.Insert(keys[n])
   160  		}
   161  
   162  		b.ResetTimer()
   163  		for n := range b.N {
   164  			filter.Delete(keys[n])
   165  		}
   166  	})
   167  }
   168  
   169  func BenchmarkInsertAndDeleteAndLookupParallel(b *testing.B) {
   170  	b.Run("preallocated", func(b *testing.B) {
   171  		keys := genKeys(b.N)
   172  		filter := prob.NewFilter(uint(b.N))
   173  
   174  		b.ResetTimer()
   175  
   176  		wg := &sync.WaitGroup{}
   177  		wg.Add(3)
   178  		go func() {
   179  			for n := range b.N {
   180  				filter.Insert(keys[n])
   181  			}
   182  			wg.Done()
   183  		}()
   184  		go func() {
   185  			for n := range b.N {
   186  				filter.Lookup(keys[n])
   187  			}
   188  			wg.Done()
   189  		}()
   190  		go func() {
   191  			for n := range b.N {
   192  				filter.Delete(keys[n])
   193  			}
   194  			wg.Done()
   195  		}()
   196  		wg.Wait()
   197  	})
   198  
   199  	b.Run("empty", func(b *testing.B) {
   200  		keys := genKeys(b.N)
   201  		filter := prob.NewFilter(10)
   202  
   203  		b.ResetTimer()
   204  
   205  		wg := &sync.WaitGroup{}
   206  		wg.Add(3)
   207  		go func() {
   208  			for n := range b.N {
   209  				filter.Insert(keys[n])
   210  			}
   211  			wg.Done()
   212  		}()
   213  		go func() {
   214  			for n := range b.N {
   215  				filter.Lookup(keys[n])
   216  			}
   217  			wg.Done()
   218  		}()
   219  		go func() {
   220  			for n := range b.N {
   221  				filter.Delete(keys[n])
   222  			}
   223  			wg.Done()
   224  		}()
   225  		wg.Wait()
   226  	})
   227  }