github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lrucache/robin_hood_test.go (about)

     1  // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package lrucache
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"runtime"
    21  	"testing"
    22  	"time"
    23  
    24  	"golang.org/x/exp/rand"
    25  )
    26  
    27  func TestRobinHoodMap(t *testing.T) {
    28  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
    29  	rhMap := newRobinHoodMap(0)
    30  	defer rhMap.free()
    31  
    32  	goMap := make(map[key]*entry)
    33  
    34  	randomKey := func() key {
    35  		n := rng.Intn(len(goMap))
    36  		for k := range goMap {
    37  			if n == 0 {
    38  				return k
    39  			}
    40  			n--
    41  		}
    42  		return key{}
    43  	}
    44  
    45  	ops := 10000 + rng.Intn(10000)
    46  	for i := 0; i < ops; i++ {
    47  		var which float64
    48  		if len(goMap) > 0 {
    49  			which = rng.Float64()
    50  		}
    51  
    52  		switch {
    53  		case which < 0.4:
    54  			var k key
    55  			k.id = rng.Uint64()
    56  			k.offset = rng.Uint64()
    57  			e := &entry{}
    58  			goMap[k] = e
    59  			rhMap.Put(k, e)
    60  			if len(goMap) != rhMap.Count() {
    61  				t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count())
    62  			}
    63  
    64  		case which < 0.1:
    65  			k := randomKey()
    66  			e := &entry{}
    67  			goMap[k] = e
    68  			rhMap.Put(k, e)
    69  			if len(goMap) != rhMap.Count() {
    70  				t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count())
    71  			}
    72  
    73  		case which < 0.75:
    74  			k := randomKey()
    75  			delete(goMap, k)
    76  			rhMap.Delete(k)
    77  			if len(goMap) != rhMap.Count() {
    78  				t.Fatalf("map sizes differ: %d != %d", len(goMap), rhMap.Count())
    79  			}
    80  
    81  		default:
    82  			k := randomKey()
    83  			v := goMap[k]
    84  			u := rhMap.Get(k)
    85  			if v != u {
    86  				t.Fatalf("%s: expected %p, but found %p", k, v, u)
    87  			}
    88  		}
    89  	}
    90  
    91  	t.Logf("map size: %d", len(goMap))
    92  }
    93  
    94  const benchSize = 1 << 20
    95  
    96  func BenchmarkGoMapInsert(b *testing.B) {
    97  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
    98  	keys := make([]key, benchSize)
    99  	for i := range keys {
   100  		keys[i].offset = uint64(rng.Intn(1 << 20))
   101  	}
   102  	b.ResetTimer()
   103  
   104  	var m map[key]*entry
   105  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   106  		if m == nil || j == len(keys) {
   107  			b.StopTimer()
   108  			m = make(map[key]*entry, len(keys))
   109  			j = 0
   110  			b.StartTimer()
   111  		}
   112  		m[keys[j]] = nil
   113  	}
   114  }
   115  
   116  func BenchmarkRobinHoodInsert(b *testing.B) {
   117  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   118  	keys := make([]key, benchSize)
   119  	for i := range keys {
   120  		keys[i].offset = uint64(rng.Intn(1 << 20))
   121  	}
   122  	e := &entry{}
   123  	b.ResetTimer()
   124  
   125  	var m *robinHoodMap
   126  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   127  		if m == nil || j == len(keys) {
   128  			b.StopTimer()
   129  			m = newRobinHoodMap(len(keys))
   130  			j = 0
   131  			b.StartTimer()
   132  		}
   133  		m.Put(keys[j], e)
   134  	}
   135  
   136  	runtime.KeepAlive(e)
   137  }
   138  
   139  func BenchmarkGoMapLookupHit(b *testing.B) {
   140  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   141  	keys := make([]key, benchSize)
   142  	m := make(map[key]*entry, len(keys))
   143  	e := &entry{}
   144  	for i := range keys {
   145  		keys[i].offset = uint64(rng.Intn(1 << 20))
   146  		m[keys[i]] = e
   147  	}
   148  	b.ResetTimer()
   149  
   150  	var p *entry
   151  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   152  		if j == len(keys) {
   153  			j = 0
   154  		}
   155  		p = m[keys[j]]
   156  	}
   157  
   158  	if testing.Verbose() {
   159  		fmt.Fprintln(ioutil.Discard, p)
   160  	}
   161  }
   162  
   163  func BenchmarkRobinHoodLookupHit(b *testing.B) {
   164  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   165  	keys := make([]key, benchSize)
   166  	m := newRobinHoodMap(len(keys))
   167  	e := &entry{}
   168  	for i := range keys {
   169  		keys[i].offset = uint64(rng.Intn(1 << 20))
   170  		m.Put(keys[i], e)
   171  	}
   172  	b.ResetTimer()
   173  
   174  	var p *entry
   175  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   176  		if j == len(keys) {
   177  			j = 0
   178  		}
   179  		p = m.Get(keys[j])
   180  	}
   181  
   182  	if testing.Verbose() {
   183  		fmt.Fprintln(ioutil.Discard, p)
   184  	}
   185  	runtime.KeepAlive(e)
   186  }
   187  
   188  func BenchmarkGoMapLookupMiss(b *testing.B) {
   189  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   190  	keys := make([]key, benchSize)
   191  	m := make(map[key]*entry, len(keys))
   192  	e := &entry{}
   193  	for i := range keys {
   194  		keys[i].id = 1
   195  		keys[i].offset = uint64(rng.Intn(1 << 20))
   196  		m[keys[i]] = e
   197  		keys[i].id = 2
   198  	}
   199  	b.ResetTimer()
   200  
   201  	var p *entry
   202  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   203  		if j == len(keys) {
   204  			j = 0
   205  		}
   206  		p = m[keys[j]]
   207  	}
   208  
   209  	if testing.Verbose() {
   210  		fmt.Fprintln(ioutil.Discard, p)
   211  	}
   212  }
   213  
   214  func BenchmarkRobinHoodLookupMiss(b *testing.B) {
   215  	rng := rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
   216  	keys := make([]key, benchSize)
   217  	m := newRobinHoodMap(len(keys))
   218  	e := &entry{}
   219  	for i := range keys {
   220  		keys[i].id = 1
   221  		keys[i].offset = uint64(rng.Intn(1 << 20))
   222  		m.Put(keys[i], e)
   223  		keys[i].id = 2
   224  	}
   225  	b.ResetTimer()
   226  
   227  	var p *entry
   228  	for i, j := 0, 0; i < b.N; i, j = i+1, j+1 {
   229  		if j == len(keys) {
   230  			j = 0
   231  		}
   232  		p = m.Get(keys[j])
   233  	}
   234  
   235  	if testing.Verbose() {
   236  		fmt.Fprintln(ioutil.Discard, p)
   237  	}
   238  	runtime.KeepAlive(e)
   239  }