github.com/influxdata/influxdb/v2@v2.7.6/tsdb/series_set_test.go (about)

     1  package tsdb
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"math/rand"
     8  	"runtime"
     9  	"sync"
    10  	"testing"
    11  )
    12  
    13  func TestSeriesIDSet_AndNot(t *testing.T) {
    14  	examples := [][3][]uint64{
    15  		{
    16  			{1, 10, 20, 30},
    17  			{10, 12, 13, 14, 20},
    18  			{1, 30},
    19  		},
    20  		{
    21  			{},
    22  			{10},
    23  			{},
    24  		},
    25  		{
    26  			{1, 10, 20, 30},
    27  			{1, 10, 20, 30},
    28  			{},
    29  		},
    30  		{
    31  			{1, 10},
    32  			{1, 10, 100},
    33  			{},
    34  		},
    35  		{
    36  			{1, 10},
    37  			{},
    38  			{1, 10},
    39  		},
    40  	}
    41  
    42  	for i, example := range examples {
    43  		t.Run(fmt.Sprint(i), func(t *testing.T) {
    44  			// Build sets.
    45  			a, b := NewSeriesIDSet(), NewSeriesIDSet()
    46  			for _, v := range example[0] {
    47  				a.Add(v)
    48  			}
    49  			for _, v := range example[1] {
    50  				b.Add(v)
    51  			}
    52  
    53  			expected := NewSeriesIDSet()
    54  			for _, v := range example[2] {
    55  				expected.Add(v)
    56  			}
    57  
    58  			got := a.AndNot(b)
    59  			if got.String() != expected.String() {
    60  				t.Fatalf("got %s, expected %s", got.String(), expected.String())
    61  			}
    62  		})
    63  	}
    64  }
    65  
    66  // Ensure that cloning is race-free.
    67  func TestSeriesIDSet_Clone_Race(t *testing.T) {
    68  	main := NewSeriesIDSet()
    69  	total := NewSeriesIDSet()
    70  	for i := uint64(0); i < 1024; i++ {
    71  		main.AddNoLock(i)
    72  		total.AddNoLock(i)
    73  	}
    74  
    75  	// One test with a closure around the main SeriesIDSet,
    76  	// so that we can run a subtest with and without COW.
    77  	test := func(t *testing.T) {
    78  		n := 10 * (runtime.NumCPU() + 1)
    79  		clones := make([]*SeriesIDSet, n)
    80  		var wg sync.WaitGroup
    81  		wg.Add(n)
    82  		for i := 1; i <= n; i++ {
    83  			go func(i int) {
    84  				defer wg.Done()
    85  				clones[i-1] = main.Clone()
    86  
    87  				for j := 0; j < 1000; j++ {
    88  					id := uint64(j + (100000 * i))
    89  					total.Add(id)
    90  					clones[i-1].AddNoLock(id)
    91  				}
    92  			}(i)
    93  		}
    94  
    95  		wg.Wait()
    96  		for _, o := range clones {
    97  			if got, exp := o.Cardinality(), uint64(2024); got != exp {
    98  				t.Errorf("got cardinality %d, expected %d", got, exp)
    99  			}
   100  		}
   101  
   102  		// The original set should be unaffected
   103  		if got, exp := main.Cardinality(), uint64(1024); got != exp {
   104  			t.Errorf("got cardinality %d, expected %d", got, exp)
   105  		}
   106  
   107  		// Merging the clones should result in only 1024 shared values.
   108  		union := NewSeriesIDSet()
   109  		for _, o := range clones {
   110  			o.ForEachNoLock(func(id uint64) {
   111  				union.AddNoLock(id)
   112  			})
   113  		}
   114  
   115  		if !union.Equals(total) {
   116  			t.Fatal("union not equal to total")
   117  		}
   118  	}
   119  	t.Run("clone", test)
   120  }
   121  
   122  var resultBool bool
   123  
   124  // Contains should be typically a constant time lookup. Example results on a laptop:
   125  //
   126  // BenchmarkSeriesIDSet_Contains/1-4 			20000000	        68.5 ns/op	       0 B/op	       0 allocs/op
   127  // BenchmarkSeriesIDSet_Contains/2-4 			20000000	        70.8 ns/op	       0 B/op	       0 allocs/op
   128  // BenchmarkSeriesIDSet_Contains/10-4         	20000000	        70.3 ns/op	       0 B/op	       0 allocs/op
   129  // BenchmarkSeriesIDSet_Contains/100-4        	20000000	        71.3 ns/op	       0 B/op	       0 allocs/op
   130  // BenchmarkSeriesIDSet_Contains/1000-4       	20000000	        80.5 ns/op	       0 B/op	       0 allocs/op
   131  // BenchmarkSeriesIDSet_Contains/10000-4      	20000000	        67.3 ns/op	       0 B/op	       0 allocs/op
   132  // BenchmarkSeriesIDSet_Contains/100000-4     	20000000	        73.1 ns/op	       0 B/op	       0 allocs/op
   133  // BenchmarkSeriesIDSet_Contains/1000000-4    	20000000	        77.3 ns/op	       0 B/op	       0 allocs/op
   134  // BenchmarkSeriesIDSet_Contains/10000000-4   	20000000	        75.3 ns/op	       0 B/op	       0 allocs/op
   135  func BenchmarkSeriesIDSet_Contains(b *testing.B) {
   136  	cardinalities := []uint64{1, 2, 10, 100, 1000, 10000, 100000, 1000000, 10000000}
   137  
   138  	for _, cardinality := range cardinalities {
   139  		// Setup...
   140  		set := NewSeriesIDSet()
   141  		for i := uint64(0); i < cardinality; i++ {
   142  			set.Add(i)
   143  		}
   144  
   145  		lookup := cardinality / 2
   146  		b.Run(fmt.Sprint(cardinality), func(b *testing.B) {
   147  			for i := 0; i < b.N; i++ {
   148  				resultBool = set.Contains(lookup)
   149  			}
   150  		})
   151  	}
   152  }
   153  
   154  var set *SeriesIDSet
   155  
   156  // Adding to a larger bitset shouldn't be significantly more expensive than adding
   157  // to a smaller one. This benchmark adds a value to different cardinality sets.
   158  //
   159  // Example results from a laptop:
   160  // BenchmarkSeriesIDSet_Add/1-4 	 		1000000	      1053 ns/op	      48 B/op	       2 allocs/op
   161  // BenchmarkSeriesIDSet_Add/2-4 	 		5000000	       303 ns/op	       0 B/op	       0 allocs/op
   162  // BenchmarkSeriesIDSet_Add/10-4         	5000000	       348 ns/op	       0 B/op	       0 allocs/op
   163  // BenchmarkSeriesIDSet_Add/100-4        	5000000	       373 ns/op	       0 B/op	       0 allocs/op
   164  // BenchmarkSeriesIDSet_Add/1000-4       	5000000	       342 ns/op	       0 B/op	       0 allocs/op
   165  func BenchmarkSeriesIDSet_AddMore(b *testing.B) {
   166  	cardinalities := []uint64{1, 2, 10, 100, 1000, 10000, 100000, 1000000, 10000000}
   167  
   168  	for _, cardinality := range cardinalities {
   169  		// Setup...
   170  		set = NewSeriesIDSet()
   171  		for i := uint64(0); i < cardinality-1; i++ {
   172  			set.Add(i)
   173  		}
   174  
   175  		b.Run(fmt.Sprint(cardinality), func(b *testing.B) {
   176  			for i := 0; i < b.N; i++ {
   177  				// Add next value
   178  				set.Add(cardinality)
   179  
   180  				b.StopTimer()
   181  				set.Remove(cardinality)
   182  				b.StartTimer()
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  // Add benchmarks the cost of adding the same element to a set versus the
   189  // cost of checking if it exists before adding it.
   190  //
   191  // Typical benchmarks from a laptop:
   192  //
   193  // BenchmarkSeriesIDSet_Add/cardinality_1000000_add/same-8    							20000000	        64.8 ns/op	       0 B/op	       0 allocs/op
   194  // BenchmarkSeriesIDSet_Add/cardinality_1000000_add/random-8  	 						 2000000	       704 	 ns/op	       5 B/op	       0 allocs/op
   195  // BenchmarkSeriesIDSet_Add/cardinality_1000000_add/same_no_lock-8         				50000000	        40.3 ns/op	       0 B/op	       0 allocs/op
   196  // BenchmarkSeriesIDSet_Add/cardinality_1000000_add/random_no_lock-8       				 2000000	       644   ns/op	       5 B/op	       0 allocs/op
   197  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/same_no_lock-8   				50000000	        34.0 ns/op	       0 B/op	       0 allocs/op
   198  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/random_no_lock-8 	 		   	 2000000	       860   ns/op	      14 B/op	       0 allocs/op
   199  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/same_global_lock-8         	30000000	        49.8 ns/op	       0 B/op	       0 allocs/op
   200  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/random_global_lock-8       	 2000000	       914   ns/op	       0 B/op	       0 allocs/op
   201  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/same_multi_lock-8          	30000000	        39.7 ns/op	       0 B/op	       0 allocs/op
   202  // BenchmarkSeriesIDSet_Add/cardinality_1000000_check_add/random_multi_lock-8        	 1000000	      1002   ns/op	       0 B/op	       0 allocs/op
   203  func BenchmarkSeriesIDSet_Add(b *testing.B) {
   204  	// Setup...
   205  	set = NewSeriesIDSet()
   206  	for i := uint64(0); i < 1000000; i++ {
   207  		set.Add(i)
   208  	}
   209  	lookup := uint64(300032)
   210  
   211  	// Add the same value over and over.
   212  	b.Run("cardinality_1000000_add", func(b *testing.B) {
   213  		b.Run("same", func(b *testing.B) {
   214  			for i := 0; i < b.N; i++ {
   215  				set.Add(lookup)
   216  			}
   217  		})
   218  
   219  		b.Run("random", func(b *testing.B) {
   220  			for i := 0; i < b.N; i++ {
   221  				b.StopTimer()
   222  				x := rand.Intn(math.MaxInt32)
   223  				b.StartTimer()
   224  				set.Add(uint64(x))
   225  			}
   226  		})
   227  
   228  		b.Run("same no lock", func(b *testing.B) {
   229  			for i := 0; i < b.N; i++ {
   230  				set.AddNoLock(lookup)
   231  			}
   232  		})
   233  
   234  		b.Run("random no lock", func(b *testing.B) {
   235  			for i := 0; i < b.N; i++ {
   236  				b.StopTimer()
   237  				x := rand.Intn(math.MaxInt32)
   238  				b.StartTimer()
   239  				set.AddNoLock(uint64(x))
   240  			}
   241  		})
   242  	})
   243  
   244  	// Add the same value over and over with no lock
   245  	b.Run("cardinality_1000000_check_add", func(b *testing.B) {
   246  		b.Run("same no lock", func(b *testing.B) {
   247  			for i := 0; i < b.N; i++ {
   248  				if !set.ContainsNoLock(lookup) {
   249  					set.AddNoLock(lookup)
   250  				}
   251  			}
   252  		})
   253  
   254  		b.Run("random no lock", func(b *testing.B) {
   255  			for i := 0; i < b.N; i++ {
   256  				b.StopTimer()
   257  				x := rand.Intn(math.MaxInt32)
   258  				b.StartTimer()
   259  				if !set.ContainsNoLock(uint64(x)) {
   260  					set.AddNoLock(uint64(x))
   261  				}
   262  			}
   263  		})
   264  
   265  		b.Run("same global lock", func(b *testing.B) {
   266  			for i := 0; i < b.N; i++ {
   267  				set.Lock()
   268  				if !set.ContainsNoLock(lookup) {
   269  					set.AddNoLock(lookup)
   270  				}
   271  				set.Unlock()
   272  			}
   273  		})
   274  
   275  		b.Run("random global lock", func(b *testing.B) {
   276  			for i := 0; i < b.N; i++ {
   277  				b.StopTimer()
   278  				x := rand.Intn(math.MaxInt32)
   279  				b.StartTimer()
   280  				set.Lock()
   281  				if !set.ContainsNoLock(uint64(x)) {
   282  					set.AddNoLock(uint64(x))
   283  				}
   284  				set.Unlock()
   285  			}
   286  		})
   287  
   288  		b.Run("same multi lock", func(b *testing.B) {
   289  			for i := 0; i < b.N; i++ {
   290  				if !set.Contains(lookup) {
   291  					set.Add(lookup)
   292  				}
   293  			}
   294  		})
   295  
   296  		b.Run("random multi lock", func(b *testing.B) {
   297  			for i := 0; i < b.N; i++ {
   298  				b.StopTimer()
   299  				x := rand.Intn(math.MaxInt32)
   300  				b.StartTimer()
   301  				if !set.Contains(uint64(x)) {
   302  					set.Add(uint64(x))
   303  				}
   304  			}
   305  		})
   306  	})
   307  }
   308  
   309  var ssResult *SeriesIDSet
   310  
   311  // Benchmark various ways of creating a copy of a bitmap. Note, Clone_COW will result
   312  // in a bitmap where future modifications will involve copies.
   313  //
   314  // Typical results from an i7 laptop.
   315  // BenchmarkSeriesIDSet_Clone/cardinality_1000/re-use/Clone-8         	   			   30000	     44171 ns/op	   47200 B/op	    1737 allocs/op
   316  // BenchmarkSeriesIDSet_Clone/cardinality_1000/re-use/Merge-8         	  			  100000	     17877 ns/op	   39008 B/op	      30 allocs/op
   317  // BenchmarkSeriesIDSet_Clone/cardinality_1000/re-use/MergeInPlace-8  	  			  200000	      7367 ns/op	       0 B/op	       0 allocs/op
   318  // BenchmarkSeriesIDSet_Clone/cardinality_1000/re-use/Add-8           	   			   10000	    137460 ns/op	   62336 B/op	    2596 allocs/op
   319  // BenchmarkSeriesIDSet_Clone/cardinality_1000/re-use/WriteTo-8       	   			   30000	     52896 ns/op	   35872 B/op	     866 allocs/op
   320  // BenchmarkSeriesIDSet_Clone/cardinality_1000/don't_re-use/Clone-8   	   			   30000	     41940 ns/op	   47200 B/op	    1737 allocs/op
   321  // BenchmarkSeriesIDSet_Clone/cardinality_1000/don't_re-use/Merge-8             	  100000	     17624 ns/op	   39008 B/op	      30 allocs/op
   322  // BenchmarkSeriesIDSet_Clone/cardinality_1000/don't_re-use/MergeInPlace-8      	  100000	     17320 ns/op	   38880 B/op	      28 allocs/op
   323  // BenchmarkSeriesIDSet_Clone/cardinality_1000/don't_re-use/Add-8               	   10000	    167544 ns/op	  101216 B/op	    2624 allocs/op
   324  // BenchmarkSeriesIDSet_Clone/cardinality_1000/don't_re-use/WriteTo-8           	   20000	     66976 ns/op	   52897 B/op	     869 allocs/op
   325  // BenchmarkSeriesIDSet_Clone/cardinality_10000/re-use/Clone-8                  	   10000	    179933 ns/op	  177072 B/op	    5895 allocs/op
   326  // BenchmarkSeriesIDSet_Clone/cardinality_10000/re-use/Merge-8                  	   20000	     77574 ns/op	  210656 B/op	      42 allocs/op
   327  // BenchmarkSeriesIDSet_Clone/cardinality_10000/re-use/MergeInPlace-8           	  100000	     23645 ns/op	       0 B/op	       0 allocs/op
   328  // BenchmarkSeriesIDSet_Clone/cardinality_10000/re-use/Add-8                    	    2000	    689254 ns/op	  224161 B/op	    9572 allocs/op
   329  // BenchmarkSeriesIDSet_Clone/cardinality_10000/re-use/WriteTo-8                	   10000	    199052 ns/op	  118791 B/op	    2945 allocs/op
   330  // BenchmarkSeriesIDSet_Clone/cardinality_10000/don't_re-use/Clone-8            	   10000	    183137 ns/op	  177073 B/op	    5895 allocs/op
   331  // BenchmarkSeriesIDSet_Clone/cardinality_10000/don't_re-use/Merge-8            	   20000	     77502 ns/op	  210656 B/op	      42 allocs/op
   332  // BenchmarkSeriesIDSet_Clone/cardinality_10000/don't_re-use/MergeInPlace-8     	   20000	     72610 ns/op	  210528 B/op	      40 allocs/op
   333  // BenchmarkSeriesIDSet_Clone/cardinality_10000/don't_re-use/Add-8              	    2000	    724789 ns/op	  434691 B/op	    9612 allocs/op
   334  // BenchmarkSeriesIDSet_Clone/cardinality_10000/don't_re-use/WriteTo-8          	   10000	    215734 ns/op	  177159 B/op	    2948 allocs/op
   335  // BenchmarkSeriesIDSet_Clone/cardinality_100000/re-use/Clone-8                 	    5000	    244971 ns/op	  377648 B/op	    6111 allocs/op
   336  // BenchmarkSeriesIDSet_Clone/cardinality_100000/re-use/Merge-8                 	   20000	     90580 ns/op	  210656 B/op	      42 allocs/op
   337  // BenchmarkSeriesIDSet_Clone/cardinality_100000/re-use/MergeInPlace-8          	   50000	     24697 ns/op	       0 B/op	       0 allocs/op
   338  // BenchmarkSeriesIDSet_Clone/cardinality_100000/re-use/Add-8                   	     500	   3274456 ns/op	  758996 B/op	   19853 allocs/op
   339  // BenchmarkSeriesIDSet_Clone/cardinality_100000/re-use/WriteTo-8               	    5000	    248791 ns/op	  122392 B/op	    3053 allocs/op
   340  // BenchmarkSeriesIDSet_Clone/cardinality_100000/don't_re-use/Clone-8           	    5000	    269152 ns/op	  377648 B/op	    6111 allocs/op
   341  // BenchmarkSeriesIDSet_Clone/cardinality_100000/don't_re-use/Merge-8           	   20000	     85948 ns/op	  210657 B/op	      42 allocs/op
   342  // BenchmarkSeriesIDSet_Clone/cardinality_100000/don't_re-use/MergeInPlace-8    	   20000	     78142 ns/op	  210528 B/op	      40 allocs/op
   343  // BenchmarkSeriesIDSet_Clone/cardinality_100000/don't_re-use/Add-8             	     500	   3123753 ns/op	  969529 B/op	   19893 allocs/op
   344  // BenchmarkSeriesIDSet_Clone/cardinality_100000/don't_re-use/WriteTo-8         	   10000	    230657 ns/op	  180684 B/op	    3056 allocs/op
   345  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/re-use/Clone-8                	    3000	    551781 ns/op	 2245424 B/op	    6111 allocs/op
   346  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/re-use/Merge-8                	   20000	     92104 ns/op	  210656 B/op	      42 allocs/op
   347  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/re-use/MergeInPlace-8         	   50000	     27408 ns/op	       0 B/op	       0 allocs/op
   348  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/re-use/Add-8                  	     100	  22573498 ns/op	 6420446 B/op	   30520 allocs/op
   349  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/re-use/WriteTo-8              	    5000	    284901 ns/op	  123522 B/op	    3053 allocs/op
   350  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/don't_re-use/Clone-8          	    3000	    679284 ns/op	 2245424 B/op	    6111 allocs/op
   351  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/don't_re-use/Merge-8          	   20000	     68965 ns/op	  210656 B/op	      42 allocs/op
   352  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/don't_re-use/MergeInPlace-8   	   20000	     64236 ns/op	  210528 B/op	      40 allocs/op
   353  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/don't_re-use/Add-8            	     100	  21960668 ns/op	 6630979 B/op	   30560 allocs/op
   354  // BenchmarkSeriesIDSet_Clone/cardinality_1000000/don't_re-use/WriteTo-8        	    5000	    298276 ns/op	  181890 B/op	    3056 allocs/op
   355  
   356  func BenchmarkSeriesIDSet_Clone(b *testing.B) {
   357  	toAddCardinalities := []int{1e3, 1e4, 1e5, 1e6}
   358  
   359  	runBenchmarks := func(b *testing.B, other *SeriesIDSet, init func() *SeriesIDSet) {
   360  		b.Run("Clone", func(b *testing.B) {
   361  			for i := 0; i < b.N; i++ {
   362  				ssResult = other.Clone()
   363  			}
   364  		})
   365  
   366  		b.Run("Merge", func(b *testing.B) {
   367  			ssResult = init()
   368  			for i := 0; i < b.N; i++ {
   369  				ssResult.Merge(other)
   370  				b.StopTimer()
   371  				ssResult = init()
   372  				b.StartTimer()
   373  			}
   374  		})
   375  
   376  		b.Run("MergeInPlace", func(b *testing.B) {
   377  			ssResult = init()
   378  			for i := 0; i < b.N; i++ {
   379  				ssResult.MergeInPlace(other)
   380  				b.StopTimer()
   381  				ssResult = init()
   382  				b.StartTimer()
   383  			}
   384  		})
   385  
   386  		b.Run("Add", func(b *testing.B) {
   387  			ssResult = init()
   388  			for i := 0; i < b.N; i++ {
   389  				itr := other.Iterator()
   390  				ssResult.Lock()
   391  				for itr.HasNext() {
   392  					ssResult.AddNoLock(uint64(itr.Next()))
   393  				}
   394  				ssResult.Unlock()
   395  				b.StopTimer()
   396  				ssResult = init()
   397  				b.StartTimer()
   398  			}
   399  		})
   400  
   401  		b.Run("WriteTo", func(b *testing.B) {
   402  			var buf bytes.Buffer
   403  			ssResult = init()
   404  			for i := 0; i < b.N; i++ {
   405  				other.WriteTo(&buf)
   406  				ssResult.UnmarshalBinaryUnsafe(buf.Bytes())
   407  				b.StopTimer()
   408  				ssResult = init()
   409  				buf.Reset()
   410  				b.StartTimer()
   411  			}
   412  		})
   413  	}
   414  
   415  	for _, toAddCardinality := range toAddCardinalities {
   416  		b.Run(fmt.Sprintf("cardinality %d", toAddCardinality), func(b *testing.B) {
   417  			ids := make([]uint64, 0, toAddCardinality)
   418  			for i := 0; i < toAddCardinality; i++ {
   419  				ids = append(ids, uint64(rand.Intn(200000000)))
   420  			}
   421  			other := NewSeriesIDSet(ids...)
   422  
   423  			b.Run("re-use", func(b *testing.B) {
   424  				base := NewSeriesIDSet()
   425  				runBenchmarks(b, other, func() *SeriesIDSet {
   426  					base.Clear()
   427  					return base
   428  				})
   429  			})
   430  
   431  			b.Run("don't re-use", func(b *testing.B) {
   432  				runBenchmarks(b, other, func() *SeriesIDSet {
   433  					return NewSeriesIDSet()
   434  				})
   435  			})
   436  		})
   437  	}
   438  }
   439  func BenchmarkSeriesIDSet_AddMany(b *testing.B) {
   440  	cardinalities := []int{1, 1e3, 1e4, 1e5, 1e6}
   441  	toAddCardinalities := []int{1e3, 1e4, 1e5}
   442  
   443  	for _, cardinality := range cardinalities {
   444  		ids := make([]uint64, 0, cardinality)
   445  		for i := 0; i < cardinality; i++ {
   446  			ids = append(ids, uint64(rand.Intn(200000000)))
   447  		}
   448  
   449  		// Setup...
   450  		set = NewSeriesIDSet(ids...)
   451  
   452  		// Check if the value exists before adding it under two locks.
   453  		b.Run(fmt.Sprintf("cardinality %d", cardinality), func(b *testing.B) {
   454  			for _, toAddCardinality := range toAddCardinalities {
   455  				ids := make([]uint64, 0, toAddCardinality)
   456  				for i := 0; i < toAddCardinality; i++ {
   457  					ids = append(ids, uint64(rand.Intn(200000000)))
   458  				}
   459  
   460  				b.Run(fmt.Sprintf("adding %d", toAddCardinality), func(b *testing.B) {
   461  					b.Run("AddNoLock", func(b *testing.B) {
   462  						clone := set.Clone()
   463  						for i := 0; i < b.N; i++ {
   464  							for _, id := range ids {
   465  								clone.AddNoLock(id)
   466  							}
   467  
   468  							b.StopTimer()
   469  							clone = set.Clone()
   470  							b.StartTimer()
   471  						}
   472  					})
   473  
   474  					b.Run("AddMany", func(b *testing.B) {
   475  						clone := set.Clone()
   476  						for i := 0; i < b.N; i++ {
   477  							clone.AddMany(ids...)
   478  							b.StopTimer()
   479  							clone = set.Clone()
   480  							b.StartTimer()
   481  						}
   482  					})
   483  
   484  					// Merge will involve a new bitmap being allocated.
   485  					b.Run("Merge", func(b *testing.B) {
   486  						clone := set.Clone()
   487  						for i := 0; i < b.N; i++ {
   488  							other := NewSeriesIDSet(ids...)
   489  							clone.Merge(other)
   490  
   491  							b.StopTimer()
   492  							clone = set.Clone()
   493  							b.StartTimer()
   494  						}
   495  					})
   496  
   497  					b.Run("MergeInPlace", func(b *testing.B) {
   498  						clone := set.Clone()
   499  						for i := 0; i < b.N; i++ {
   500  							other := NewSeriesIDSet(ids...)
   501  							clone.MergeInPlace(other)
   502  
   503  							b.StopTimer()
   504  							clone = set.Clone()
   505  							b.StartTimer()
   506  						}
   507  					})
   508  				})
   509  
   510  			}
   511  		})
   512  	}
   513  }
   514  
   515  // Remove benchmarks the cost of removing the same element in a set versus the
   516  // cost of checking if it exists before removing it.
   517  //
   518  // Typical benchmarks from a laptop:
   519  //
   520  // BenchmarkSeriesIDSet_Remove/cardinality_1000000_remove_same-4         		20000000	        99.1 ns/op	       0 B/op	       0 allocs/op
   521  // BenchmarkSeriesIDSet_Remove/cardinality_1000000_check_remove_global_lock-4   20000000	        57.7 ns/op	       0 B/op	       0 allocs/op
   522  // BenchmarkSeriesIDSet_Remove/cardinality_1000000_check_remove_multi_lock-4    20000000	        80.1 ns/op	       0 B/op	       0 allocs/op
   523  func BenchmarkSeriesIDSet_Remove(b *testing.B) {
   524  	// Setup...
   525  	set = NewSeriesIDSet()
   526  	for i := uint64(0); i < 1000000; i++ {
   527  		set.Add(i)
   528  	}
   529  	lookup := uint64(300032)
   530  
   531  	// Remove the same value over and over.
   532  	b.Run("cardinality_1000000_remove_same", func(b *testing.B) {
   533  		for i := 0; i < b.N; i++ {
   534  			set.Remove(lookup)
   535  		}
   536  	})
   537  
   538  	// Check if the value exists before adding it. Subsequent repeats of the code
   539  	// will result in contains checks.
   540  	b.Run("cardinality_1000000_check_remove_global_lock", func(b *testing.B) {
   541  		for i := 0; i < b.N; i++ {
   542  			set.Lock()
   543  			if set.ContainsNoLock(lookup) {
   544  				set.RemoveNoLock(lookup)
   545  			}
   546  			set.Unlock()
   547  		}
   548  	})
   549  
   550  	// Check if the value exists before adding it under two locks.
   551  	b.Run("cardinality_1000000_check_remove_multi_lock", func(b *testing.B) {
   552  		for i := 0; i < b.N; i++ {
   553  			if set.Contains(lookup) {
   554  				set.Remove(lookup)
   555  			}
   556  		}
   557  	})
   558  }
   559  
   560  // Typical benchmarks for a laptop:
   561  //
   562  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1/shards_1-4         	  200000	      8095 ns/op	   16656 B/op	      11 allocs/op
   563  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1/shards_10-4        	  200000	     11755 ns/op	   18032 B/op	      47 allocs/op
   564  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1/shards_100-4       	   50000	     41632 ns/op	   31794 B/op	     407 allocs/op
   565  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000/shards_1-4     	  200000	      6022 ns/op	    8384 B/op	       7 allocs/op
   566  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000/shards_10-4    	  100000	     19674 ns/op	    9760 B/op	      43 allocs/op
   567  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000/shards_100-4   	   10000	    152865 ns/op	   23522 B/op	     403 allocs/op
   568  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1000000/shards_1-4   	  200000	      8252 ns/op	    9712 B/op	      44 allocs/op
   569  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1000000/shards_10-4  	   50000	     29566 ns/op	   15984 B/op	     143 allocs/op
   570  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_1000000/shards_100-4 	   10000	    237672 ns/op	   78710 B/op	    1133 allocs/op
   571  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000000/shards_1-4  	  100000	     21559 ns/op	   25968 B/op	     330 allocs/op
   572  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000000/shards_10-4 	   20000	    102326 ns/op	  114325 B/op	     537 allocs/op
   573  // BenchmarkSeriesIDSet_Merge_Duplicates/cardinality_10000000/shards_100-4      2000	   1042697 ns/op	  997909 B/op	    2608 allocs/op
   574  func BenchmarkSeriesIDSet_Merge_Duplicates(b *testing.B) {
   575  	cardinalities := []int{1, 10000, 1000000, 10000000}
   576  	shards := []int{1, 10, 100}
   577  
   578  	for _, cardinality := range cardinalities {
   579  		set = NewSeriesIDSet()
   580  		for i := 0; i < cardinality; i++ {
   581  			set.Add(uint64(i))
   582  		}
   583  
   584  		for _, shard := range shards {
   585  			others := make([]*SeriesIDSet, 0, shard)
   586  			for s := 0; s < shard; s++ {
   587  				others = append(others, &SeriesIDSet{bitmap: set.bitmap.Clone()})
   588  			}
   589  
   590  			b.Run(fmt.Sprintf("cardinality_%d/shards_%d", cardinality, shard), func(b *testing.B) {
   591  				base := &SeriesIDSet{bitmap: set.bitmap.Clone()}
   592  				for i := 0; i < b.N; i++ {
   593  					base.Merge(others...)
   594  					b.StopTimer()
   595  					base.bitmap = set.bitmap.Clone()
   596  					b.StartTimer()
   597  				}
   598  			})
   599  
   600  		}
   601  	}
   602  }
   603  
   604  // Typical benchmarks for a laptop:
   605  //
   606  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1/shards_1-4         	  200000	      7841 ns/op	   16656 B/op	      11 allocs/op
   607  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1/shards_10-4        	  200000	     13093 ns/op	   18048 B/op	      47 allocs/op
   608  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1/shards_100-4       	   30000	     57399 ns/op	   31985 B/op	     407 allocs/op
   609  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000/shards_1-4     	  200000	      7740 ns/op	    8384 B/op	       7 allocs/op
   610  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000/shards_10-4    	   50000	     37116 ns/op	   18208 B/op	      52 allocs/op
   611  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000/shards_100-4   	    5000	    409487 ns/op	  210563 B/op	     955 allocs/op
   612  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1000000/shards_1-4   	  100000	     19289 ns/op	   19328 B/op	      79 allocs/op
   613  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1000000/shards_10-4  	   10000	    129048 ns/op	  159716 B/op	     556 allocs/op
   614  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_1000000/shards_100-4 	     500	   3482907 ns/op	 5428116 B/op	    6174 allocs/op
   615  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000000/shards_1-4  	   30000	     43734 ns/op	   51872 B/op	     641 allocs/op
   616  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000000/shards_10-4 	    3000	    514412 ns/op	  748678 B/op	    3687 allocs/op
   617  // BenchmarkSeriesIDSet_Merge_Unique/cardinality_10000000/shards_100-4         	      30	  61891687 ns/op	69626539 B/op	   36038 allocs/op
   618  func BenchmarkSeriesIDSet_Merge_Unique(b *testing.B) {
   619  	cardinalities := []int{1, 10000, 1000000, 10000000}
   620  	shards := []int{1, 10, 100}
   621  
   622  	for _, cardinality := range cardinalities {
   623  		set = NewSeriesIDSet()
   624  		for i := 0; i < cardinality; i++ {
   625  			set.Add(uint64(i))
   626  		}
   627  
   628  		for _, shard := range shards {
   629  			others := make([]*SeriesIDSet, 0, shard)
   630  			for s := 1; s <= shard; s++ {
   631  				other := NewSeriesIDSet()
   632  				for i := 0; i < cardinality; i++ {
   633  					other.Add(uint64(i + (s * cardinality)))
   634  				}
   635  				others = append(others, other)
   636  			}
   637  
   638  			b.Run(fmt.Sprintf("cardinality_%d/shards_%d", cardinality, shard), func(b *testing.B) {
   639  				base := &SeriesIDSet{bitmap: set.bitmap.Clone()}
   640  				for i := 0; i < b.N; i++ {
   641  					base.Merge(others...)
   642  					b.StopTimer()
   643  					base.bitmap = set.bitmap.Clone()
   644  					b.StartTimer()
   645  				}
   646  			})
   647  		}
   648  	}
   649  }