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  }