github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/pkg/field/searchable_fields_test.go (about)

     1  // Copyright 2022 Meta Platforms, Inc. and affiliates.
     2  //
     3  // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
     4  //
     5  // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     6  //
     7  // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
     8  //
     9  // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    10  //
    11  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    12  
    13  //go:build go_obs_experimental
    14  // +build go_obs_experimental
    15  
    16  package field
    17  
    18  import (
    19  	"fmt"
    20  	"math/rand"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  )
    25  
    26  func TestSearchableFieldsDeduplicate(t *testing.T) {
    27  	type testCase struct {
    28  		Input  Fields
    29  		Expect Fields
    30  	}
    31  
    32  	for testCaseID, testCase := range []testCase{
    33  		{
    34  			Input:  Fields{},
    35  			Expect: Fields{},
    36  		},
    37  		{
    38  			Input: Fields{
    39  				{
    40  					Key:   "1",
    41  					Value: 2,
    42  				},
    43  			},
    44  			Expect: Fields{
    45  				{
    46  					Key:   "1",
    47  					Value: 2,
    48  				},
    49  			},
    50  		},
    51  		{
    52  			Input: Fields{
    53  				{
    54  					Key:   "1",
    55  					Value: 2,
    56  				},
    57  				{
    58  					Key:   "1",
    59  					Value: 3,
    60  				},
    61  			},
    62  			Expect: Fields{
    63  				{
    64  					Key:   "1",
    65  					Value: 3,
    66  				},
    67  			},
    68  		},
    69  		{
    70  			Input: Fields{
    71  				{
    72  					Key:   "0",
    73  					Value: 0,
    74  				},
    75  				{
    76  					Key:   "1",
    77  					Value: 2,
    78  				},
    79  				{
    80  					Key:   "1",
    81  					Value: 3,
    82  				},
    83  			},
    84  			Expect: Fields{
    85  				{
    86  					Key:   "0",
    87  					Value: 0,
    88  				},
    89  				{
    90  					Key:   "1",
    91  					Value: 3,
    92  				},
    93  			},
    94  		},
    95  		{
    96  			Input: Fields{
    97  				{
    98  					Key:   "1",
    99  					Value: 2,
   100  				},
   101  				{
   102  					Key:   "1",
   103  					Value: 3,
   104  				},
   105  				{
   106  					Key:   "4",
   107  					Value: 5,
   108  				},
   109  			},
   110  			Expect: Fields{
   111  				{
   112  					Key:   "1",
   113  					Value: 3,
   114  				},
   115  				{
   116  					Key:   "4",
   117  					Value: 5,
   118  				},
   119  			},
   120  		},
   121  		{
   122  			Input: Fields{
   123  				{
   124  					Key:   "4",
   125  					Value: 5,
   126  				},
   127  				{
   128  					Key:   "1",
   129  					Value: 2,
   130  				},
   131  				{
   132  					Key:   "1",
   133  					Value: 3,
   134  				},
   135  			},
   136  			Expect: Fields{
   137  				{
   138  					Key:   "1",
   139  					Value: 3,
   140  				},
   141  				{
   142  					Key:   "4",
   143  					Value: 5,
   144  				},
   145  			},
   146  		},
   147  	} {
   148  		s := NewSearchableFields(testCase.Input)
   149  		s.Build()
   150  		assert.Equal(t, testCase.Expect, s.Fields(), fmt.Sprintf("test case #%d", testCaseID))
   151  	}
   152  }
   153  
   154  func BenchmarkSearchableFieldsDeduplicateKeys(b *testing.B) {
   155  	for _, isSorted := range []bool{false, true} {
   156  		b.Run(fmt.Sprintf("isSorted-%v", isSorted), func(b *testing.B) {
   157  			maxSize := 1024 * 1024
   158  			for totalSize := 1; totalSize <= maxSize; totalSize *= 2 {
   159  				b.Run(fmt.Sprintf("fields%d", totalSize), func(b *testing.B) {
   160  					for duplicatesPlus1 := 1; duplicatesPlus1 <= maxSize; duplicatesPlus1 *= 2 {
   161  						if duplicatesPlus1 > totalSize {
   162  							b.Skip()
   163  							return
   164  						}
   165  						duplicates := duplicatesPlus1 - 1
   166  						b.Run(fmt.Sprintf("dups%d", duplicates), func(b *testing.B) {
   167  							var fields SearchableFields
   168  							fields.fields = make(Fields, totalSize)
   169  							idxs := rand.Perm(totalSize)
   170  							for idx := range idxs[duplicates:] {
   171  								fields.fields[idx] = Field{
   172  									Key:   fmt.Sprint(idx),
   173  									Value: idx,
   174  								}
   175  							}
   176  							for idx := range idxs[:duplicates] {
   177  								dupOfIdx := rand.Intn(len(idxs[duplicates:]))
   178  								fields.fields[idx] = fields.fields[idxs[dupOfIdx]]
   179  							}
   180  
   181  							if isSorted {
   182  								fields.sort()
   183  							}
   184  							b.ReportAllocs()
   185  							b.ResetTimer()
   186  							for i := 0; i < b.N; i++ {
   187  								b.StopTimer()
   188  								fields := fields.Copy()
   189  								b.StartTimer()
   190  								fields.Build()
   191  							}
   192  						})
   193  					}
   194  				})
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func BenchmarkSearchableFieldsAddGet(b *testing.B) {
   201  	for _, fieldsCount := range []uint{1, 2, 4, 8, 16, 32, 33, 64, 128, 256, 512, 1024} {
   202  		b.Run(fmt.Sprintf("fieldCount%d", fieldsCount), func(b *testing.B) {
   203  			for _, getCount := range []uint{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024} {
   204  				b.Run(fmt.Sprintf("getCount%v", getCount), func(b *testing.B) {
   205  					_fields := dummyFields(fieldsCount + getCount)
   206  					b.ReportAllocs()
   207  					b.ResetTimer()
   208  					for i := 0; i < b.N; i++ {
   209  						fields := NewSearchableFields(nil)
   210  						for idx := uint(0); idx < fieldsCount; idx++ {
   211  							fields.Add(_fields[idx])
   212  						}
   213  						for idx := 0; idx < int(getCount); idx++ {
   214  							fields.Get(_fields[getCount].Key)
   215  						}
   216  					}
   217  				})
   218  			}
   219  		})
   220  	}
   221  }
   222  
   223  func BenchmarkSearchableFields_CopyAndAddOne(b *testing.B) {
   224  	fields := Gather(testContextField)
   225  	for _, withGet := range []bool{false, true} {
   226  		b.Run(fmt.Sprintf("withGet-%v", withGet), func(b *testing.B) {
   227  			for _, cloneDepth := range []uint{1, 2, 4, 8, 16, 32, 64} {
   228  				_fields := dummyFields(cloneDepth)
   229  				b.Run(fmt.Sprintf("cloneDepth%d", cloneDepth), func(b *testing.B) {
   230  					initialFields := NewSearchableFields(fields.Copy())
   231  					b.ReportAllocs()
   232  					b.ResetTimer()
   233  					for i := 0; i < b.N; i++ {
   234  						f := initialFields
   235  						for i := 0; i < int(cloneDepth); i++ {
   236  							f = f.Copy()
   237  							f.Add(_fields[i])
   238  						}
   239  						if withGet {
   240  							f.Get(_fields[0].Key)
   241  						}
   242  					}
   243  				})
   244  			}
   245  		})
   246  	}
   247  }