github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/bench/maplookup/lookup_test.go (about) 1 package maplookup_test 2 3 import ( 4 _ "embed" 5 "fmt" 6 "math/rand" 7 "sort" 8 "strings" 9 "testing" 10 11 "github.com/egonelbre/exp/wordsearch/trie-compact" 12 ) 13 14 type LookupList []string 15 16 func (q LookupList) Contains(x string) bool { 17 for _, t := range q { 18 if t == x { 19 return true 20 } 21 } 22 return false 23 } 24 25 type BinaryList []string 26 27 func (x BinaryList) Contains(target string) bool { 28 n := len(x) 29 i, j := 0, n 30 for i < j { 31 h := int(uint(i+j) >> 1) 32 if x[h] < target { 33 i = h + 1 34 } else { 35 j = h 36 } 37 } 38 39 return i < n && x[i] == target 40 } 41 42 func lookupList(entries []string) LookupList { 43 sort.Strings(entries) 44 full := strings.Join(entries, "") 45 list := []string{} 46 for _, s := range entries { 47 list = append(list, full[:len(s)]) 48 full = full[len(s):] 49 } 50 return LookupList(list) 51 } 52 53 func binaryList(entries []string) BinaryList { 54 return BinaryList(lookupList(entries)) 55 } 56 57 type LookupMap map[string]struct{} 58 59 func (q LookupMap) Contains(x string) bool { 60 _, ok := q[x] 61 return ok 62 } 63 64 func lookupTrie(entries []string) *trie.Compact { 65 root := trie.Uncompact{} 66 for _, x := range entries { 67 root.Insert(x) 68 } 69 return root.Compress() 70 } 71 72 func lookupMap(entries []string) LookupMap { 73 xs := make(map[string]struct{}) 74 for _, x := range entries { 75 q := "x" + x + "x" 76 xs[q[1:len(q)-1]] = struct{}{} 77 } 78 return LookupMap(xs) 79 } 80 81 func lookupMaph(entries []string) LookupMap { 82 xs := make(map[string]struct{}, len(entries)) 83 for _, x := range entries { 84 q := "x" + x + "x" 85 xs[q[1:len(q)-1]] = struct{}{} 86 } 87 return LookupMap(xs) 88 } 89 90 var randomStrings []string 91 var sizes = []int{1, 4, 6, 8, 10, 12, 14, 16, 18, 24, 28, 32} 92 93 const maxLookup = 10000 94 95 //go:embed enable1.txt 96 var dictionary []byte 97 98 func init() { 99 randomStrings = strings.Split(string(dictionary), "\n") 100 shuffleStrings(randomStrings) 101 } 102 103 const queryCount = 1000 104 105 type scenario struct { 106 name string 107 query []string 108 } 109 110 func scenarios(targets, mismatches []string) []scenario { 111 return []scenario{ 112 { 113 name: "100%", 114 query: pickRandom(targets, queryCount), 115 }, 116 { 117 name: "90%", 118 query: concat( 119 pickRandom(targets, queryCount*90/100), 120 pickRandom(mismatches, queryCount*10/100), 121 ), 122 }, 123 { 124 name: "50%", 125 query: concat( 126 pickRandom(targets, queryCount*50/100), 127 pickRandom(mismatches, queryCount*50/100), 128 ), 129 }, 130 { 131 name: "10%", 132 query: concat( 133 pickRandom(targets, queryCount*10/100), 134 pickRandom(mismatches, queryCount*90/100), 135 ), 136 }, 137 } 138 } 139 140 var z int 141 142 func Benchmark(b *testing.B) { 143 for _, size := range sizes { 144 targets := randomStrings[:size] 145 mismatches := randomStrings[size:] 146 147 lisx := lookupList(targets) 148 binx := binaryList(targets) 149 trix := lookupTrie(targets) 150 mapx := lookupMap(targets) 151 maphx := lookupMaph(targets) 152 153 for _, scenario := range scenarios(targets, mismatches) { 154 b.Run(fmt.Sprintf("Lis/%d-%s", size, scenario.name), func(b *testing.B) { 155 for i := 0; i < b.N; i++ { 156 for _, q := range scenario.query { 157 if lisx.Contains(q) { 158 z++ 159 } 160 } 161 } 162 }) 163 164 b.Run(fmt.Sprintf("Bin/%d-%s", size, scenario.name), func(b *testing.B) { 165 for i := 0; i < b.N; i++ { 166 for _, q := range scenario.query { 167 if binx.Contains(q) { 168 z++ 169 } 170 } 171 } 172 }) 173 174 b.Run(fmt.Sprintf("Tri/%d-%s", size, scenario.name), func(b *testing.B) { 175 for i := 0; i < b.N; i++ { 176 for _, q := range scenario.query { 177 if trix.Contains(q) { 178 z++ 179 } 180 } 181 } 182 }) 183 184 b.Run(fmt.Sprintf("Map/%d-%s", size, scenario.name), func(b *testing.B) { 185 for i := 0; i < b.N; i++ { 186 for _, q := range scenario.query { 187 if mapx.Contains(q) { 188 z++ 189 } 190 } 191 } 192 }) 193 194 b.Run(fmt.Sprintf("Maph/%d-%s", size, scenario.name), func(b *testing.B) { 195 for i := 0; i < b.N; i++ { 196 for _, q := range scenario.query { 197 if maphx.Contains(q) { 198 z++ 199 } 200 } 201 } 202 }) 203 } 204 } 205 206 b.Log(z) 207 } 208 209 func concat(xs ...[]string) []string { 210 all := []string{} 211 for _, x := range xs { 212 all = append(all, x...) 213 } 214 return all 215 } 216 217 func pickRandom(xs []string, n int) []string { 218 all := append([]string{}, xs...) 219 for len(all) < n { 220 all = append(all, all...) 221 } 222 all = all[:n] 223 shuffleStrings(all) 224 return all 225 } 226 227 func shuffleStrings(xs []string) { 228 rand.Shuffle(len(xs), func(i, k int) { 229 xs[i], xs[k] = xs[k], xs[i] 230 }) 231 }