github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/perf/lru/cache_test.go (about)

     1  package lru
     2  
     3  import (
     4  	"math/rand"
     5  	"runtime"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  func createFilledCache(ttl time.Duration) *Cache[int64, int64] {
    11  	c := NewCache[int64, int64](1000)
    12  	for i := 0; i < 1000; i++ {
    13  		key := int64(rand.Intn(5000))
    14  		c.Set(key, key, ttl)
    15  	}
    16  	return c
    17  }
    18  
    19  func createRandInts(size int) []int64 {
    20  	s := make([]int64, size)
    21  	for i := 0; i < size; i++ {
    22  		s[i] = rand.Int63n(5000)
    23  	}
    24  	return s
    25  }
    26  
    27  func TestBasicEviction(t *testing.T) {
    28  	t.Parallel()
    29  	c := NewCache[string, string](3)
    30  	if _, ok, _ := c.Get("a"); ok {
    31  		t.Error("a")
    32  	}
    33  
    34  	c.Set("b", "vb", 2*time.Second)
    35  	c.Set("a", "va", time.Second)
    36  	c.Set("c", "vc", 3*time.Second)
    37  
    38  	if v, _, _ := c.Get("a"); v != "va" {
    39  		t.Error("va")
    40  	}
    41  	if v, _, _ := c.Get("b"); v != "vb" {
    42  		t.Error("vb")
    43  	}
    44  	if v, _, _ := c.Get("c"); v != "vc" {
    45  		t.Error("vc")
    46  	}
    47  
    48  	c.Set("d", "vd", time.Second)
    49  	if _, ok, _ := c.Get("a"); ok {
    50  		t.Error("expecting element A to be evicted")
    51  	}
    52  	c.Set("e", "ve", time.Second)
    53  	if _, ok, _ := c.Get("b"); ok {
    54  		t.Error("expecting element B to be evicted")
    55  	}
    56  	c.Set("f", "vf", time.Second)
    57  	if _, ok, _ := c.Get("c"); ok {
    58  		t.Error("expecting element C to be evicted")
    59  	}
    60  
    61  	if v, _, _ := c.Get("d"); v != "vd" {
    62  		t.Error("expecting element D to not be evicted")
    63  	}
    64  
    65  	// e, f, d, [g]
    66  	c.Set("g", "vg", time.Second)
    67  	if _, ok, _ := c.Get("E"); ok {
    68  		t.Error("expecting element E to be evicted")
    69  	}
    70  
    71  	if l := c.Len(); l != 3 {
    72  		t.Errorf("invalid length, want= 3, got= %v", l)
    73  	}
    74  
    75  	c.Delete("missing")
    76  	c.Delete("g")
    77  	if l := c.Len(); l != 2 {
    78  		t.Errorf("invalid length, want= 2, got= %v", l)
    79  	}
    80  
    81  	// f, d, [h, i]
    82  	c.MSet(map[string]string{"h": "vh", "i": "vi"}, time.Second)
    83  	if _, ok, _ := c.Get("e"); ok {
    84  		t.Error("expecting element E to be evicted")
    85  	}
    86  	if _, ok, _ := c.Get("f"); ok {
    87  		t.Error("expecting element F to be evicted")
    88  	}
    89  	if v, _, _ := c.Get("d"); v != "vd" {
    90  		t.Error("expecting element D to not be evicted")
    91  	}
    92  
    93  	// h/i, i/h, d, [h, i]
    94  	m := c.MGet("h", "i")
    95  	if m["h"] != "vh" {
    96  		t.Error("expecting MSet and MGet to work")
    97  	}
    98  	if m["i"] != "vi" {
    99  		t.Error("expecting MSet and MGet to work")
   100  	}
   101  
   102  	if v, _, _ := c.GetQuiet("d"); v != "vd" {
   103  		t.Error("expecting GetQuiet to work")
   104  	}
   105  
   106  	if v, _ := c.GetNotStale("d"); v != "vd" {
   107  		t.Error("expecting GetNotStale to work")
   108  	}
   109  }
   110  
   111  func TestConcurrentGet(t *testing.T) {
   112  	t.Parallel()
   113  	c := createFilledCache(time.Second)
   114  	s := createRandInts(50000)
   115  
   116  	runConcurrentGetTest(t, c, s)
   117  }
   118  
   119  func runConcurrentGetTest(t *testing.T, c Interface[int64, int64], s []int64) {
   120  	done := make(chan bool)
   121  	worker := func() {
   122  		for i := 0; i < 5000; i++ {
   123  			key := s[i]
   124  			v, exists, _ := c.Get(key)
   125  			if exists && v != key {
   126  				t.Errorf("value not match: want= %v, got= %v", key, v)
   127  			}
   128  		}
   129  		done <- true
   130  	}
   131  	workers := 4
   132  	for i := 0; i < workers; i++ {
   133  		go worker()
   134  	}
   135  	for i := 0; i < workers; i++ {
   136  		_ = <-done
   137  	}
   138  }
   139  
   140  func TestConcurrentSet(t *testing.T) {
   141  	t.Parallel()
   142  	c := createFilledCache(time.Second)
   143  	s := createRandInts(5000)
   144  
   145  	runConcurrentSetTest(t, c, s)
   146  }
   147  
   148  func runConcurrentSetTest(t *testing.T, c Interface[int64, int64], s []int64) {
   149  	done := make(chan bool)
   150  	worker := func() {
   151  		ttl := 4 * time.Second
   152  		for i := 0; i < 5000; i++ {
   153  			key := s[i]
   154  			c.Set(key, key, ttl)
   155  		}
   156  		done <- true
   157  	}
   158  	workers := 4
   159  	for i := 0; i < workers; i++ {
   160  		go worker()
   161  	}
   162  	for i := 0; i < workers; i++ {
   163  		_ = <-done
   164  	}
   165  }
   166  
   167  func TestConcurrentGetSet(t *testing.T) {
   168  	t.Parallel()
   169  	c := createFilledCache(time.Second)
   170  	s := createRandInts(5000)
   171  
   172  	runConcurrentGetSetTest(t, c, s)
   173  }
   174  
   175  func runConcurrentGetSetTest(t *testing.T, c Interface[int64, int64], s []int64) {
   176  	done := make(chan bool)
   177  	getWorker := func() {
   178  		for i := 0; i < 5000; i++ {
   179  			key := s[i]
   180  			v, exists, _ := c.Get(key)
   181  			if exists && v != key {
   182  				t.Errorf("value not match: want= %v, got= %v", key, v)
   183  			}
   184  		}
   185  		done <- true
   186  	}
   187  	setWorker := func() {
   188  		ttl := 4 * time.Second
   189  		for i := 0; i < 5000; i++ {
   190  			key := s[i]
   191  			c.Set(key, key, ttl)
   192  		}
   193  		done <- true
   194  	}
   195  	workers := 4
   196  	for i := 0; i < workers; i++ {
   197  		go getWorker()
   198  		go setWorker()
   199  	}
   200  	for i := 0; i < workers*2; i++ {
   201  		_ = <-done
   202  	}
   203  }
   204  
   205  func BenchmarkConcurrentGetLRUCache(bb *testing.B) {
   206  	c := createFilledCache(time.Second)
   207  	s := createRandInts(5000)
   208  
   209  	bb.ReportAllocs()
   210  	bb.ResetTimer()
   211  	cpu := runtime.GOMAXPROCS(0)
   212  	ch := make(chan bool)
   213  	worker := func() {
   214  		for i := 0; i < bb.N/cpu; i++ {
   215  			c.Get(s[i%5000])
   216  		}
   217  		ch <- true
   218  	}
   219  	for i := 0; i < cpu; i++ {
   220  		go worker()
   221  	}
   222  	for i := 0; i < cpu; i++ {
   223  		_ = <-ch
   224  	}
   225  }
   226  
   227  func BenchmarkConcurrentSetLRUCache(bb *testing.B) {
   228  	c := createFilledCache(time.Second)
   229  	s := createRandInts(5000)
   230  
   231  	bb.ReportAllocs()
   232  	bb.ResetTimer()
   233  	cpu := runtime.GOMAXPROCS(0)
   234  	ch := make(chan bool)
   235  	worker := func() {
   236  		ttl := 4 * time.Second
   237  		for i := 0; i < bb.N/cpu; i++ {
   238  			key := s[i%5000]
   239  			c.Set(key, key, ttl)
   240  		}
   241  		ch <- true
   242  	}
   243  	for i := 0; i < cpu; i++ {
   244  		go worker()
   245  	}
   246  	for i := 0; i < cpu; i++ {
   247  		_ = <-ch
   248  	}
   249  }
   250  
   251  // No expiry
   252  func BenchmarkConcurrentSetNXLRUCache(bb *testing.B) {
   253  	c := createFilledCache(time.Second)
   254  	s := createRandInts(5000)
   255  
   256  	bb.ReportAllocs()
   257  	bb.ResetTimer()
   258  	cpu := runtime.GOMAXPROCS(0)
   259  	ch := make(chan bool)
   260  	worker := func() {
   261  		for i := 0; i < bb.N/cpu; i++ {
   262  			key := s[i%5000]
   263  			c.Set(key, key, 0)
   264  		}
   265  		ch <- true
   266  	}
   267  	for i := 0; i < cpu; i++ {
   268  		go worker()
   269  	}
   270  	for i := 0; i < cpu; i++ {
   271  		_ = <-ch
   272  	}
   273  }