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 }