github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/cachekit/core_test.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package cachekit
     7  
     8  import (
     9  	"math"
    10  	"strconv"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  func TestBasicLimits(t *testing.T) {
    17  	c := NewUintCache(newStrategy(5, 15, false, true))
    18  
    19  	for i := 1; i <= 20; i++ {
    20  		s := strconv.Itoa(i)
    21  		c.Put(uint64(i), "V" + s)
    22  	}
    23  
    24  	require.Equal(t, 15, c.Occupied())
    25  	require.Equal(t, 15, c.core.Occupied())
    26  	require.Equal(t, 20, c.Allocated())
    27  
    28  	for i := 1; i <= 5; i++ {
    29  		require.False(t, c.Contains(uint64(i)), i)
    30  	}
    31  
    32  	for i := 6; i <= 20; i++ {
    33  		s := strconv.Itoa(i)
    34  		require.True(t, c.Contains(uint64(i)), i)
    35  		v, ok := c.Peek(uint64(i))
    36  		require.True(t, ok, i)
    37  		require.Equal(t, "V" + s, v)
    38  	}
    39  }
    40  
    41  func TestDelete(t *testing.T) {
    42  	c := NewUintCache(newStrategy(5, 15, false, true))
    43  
    44  	for i := 1; i <= 15; i++ {
    45  		s := strconv.Itoa(i)
    46  		c.Put(uint64(i), "V" + s)
    47  	}
    48  
    49  	c.Delete(8)
    50  	require.False(t, c.Contains(8))
    51  
    52  	for i := 16; i <= 20; i++ {
    53  		s := strconv.Itoa(i)
    54  		c.Put(uint64(i), "V" + s)
    55  	}
    56  
    57  	require.Equal(t, 14, c.Occupied())
    58  	require.Equal(t, 15, c.core.Occupied())
    59  	require.Equal(t, 20, c.Allocated())
    60  
    61  	// this loop forces creation of generations and cleanup of them
    62  	// so the deleted item can be collected
    63  	for i := 100; i > 0; i-- {
    64  		c.Get(19)
    65  		c.Get(20)
    66  	}
    67  
    68  	require.Equal(t, 14, c.core.Occupied())
    69  
    70  	for i := 1; i <= 5; i++ {
    71  		require.False(t, c.Contains(uint64(i)), i)
    72  	}
    73  
    74  	for i := 6; i <= 7; i++ {
    75  		s := strconv.Itoa(i)
    76  		require.True(t, c.Contains(uint64(i)), i)
    77  		v, ok := c.Peek(uint64(i))
    78  		require.True(t, ok, i)
    79  		require.Equal(t, "V" + s, v)
    80  	}
    81  
    82  	require.False(t, c.Contains(8))
    83  
    84  	for i := 9; i <= 20; i++ {
    85  		s := strconv.Itoa(i)
    86  		require.True(t, c.Contains(uint64(i)), i)
    87  		v, ok := c.Peek(uint64(i))
    88  		require.True(t, ok, i)
    89  		require.Equal(t, "V" + s, v)
    90  	}
    91  
    92  	// it uses the empty slot released by deletion
    93  	c.Put(21, "V21")
    94  
    95  	require.Equal(t, 15, c.Occupied())
    96  	require.Equal(t, 15, c.core.Occupied())
    97  	require.Equal(t, 20, c.Allocated())
    98  }
    99  
   100  func TestCounter(t *testing.T) {
   101  	c := NewUintCache(newStrategy(2, 2, false, true))
   102  	c.Put(1, "A")
   103  	c.Put(2, "B")
   104  	for i := 100; i > 0; i-- {
   105  		c.Get(1)
   106  		c.Get(2)
   107  	}
   108  
   109  	require.Less(t, int32(2), *c.core.alloc.get(1))
   110  
   111  	c.core.trimGenerations(math.MaxUint32)
   112  
   113  	require.EqualValues(t, 2, *c.core.alloc.get(1))
   114  }
   115  
   116  func TestFencedCounter(t *testing.T) {
   117  	c := NewUintCache(newStrategy(4, 2, true, true))
   118  	c.Put(1, "A")
   119  	c.Put(2, "B")
   120  	for i := 100; i > 0; i-- {
   121  		c.Get(1)
   122  		c.Get(2)
   123  	}
   124  
   125  	require.EqualValues(t, 2, *c.core.alloc.get(1))
   126  
   127  	c.core.trimGenerations(math.MaxUint32)
   128  
   129  	require.EqualValues(t, 2, *c.core.alloc.get(1))
   130  }
   131  
   132  func TestTouch(t *testing.T) {
   133  	c := NewUintCache(newStrategy(5, 15, true, true))
   134  
   135  	for i := 1; i <= 20; i++ {
   136  		s := strconv.Itoa(i)
   137  		c.Put(uint64(i), "V" + s)
   138  
   139  		_, _ = c.Peek(4)
   140  		v, ok := c.Get(5)
   141  
   142  		if i < 5 {
   143  			require.False(t, ok, i)
   144  		} else {
   145  			require.True(t, ok, i)
   146  			require.Equal(t, "V5", v)
   147  		}
   148  	}
   149  
   150  	require.Equal(t, 15, c.Occupied())
   151  	require.Equal(t, 15, c.core.Occupied())
   152  	require.Equal(t, 20, c.Allocated())
   153  
   154  	for i := 1; i <= 4; i++ {
   155  		require.False(t, c.Contains(uint64(i)), i)
   156  	}
   157  
   158  	require.True(t, c.Contains(5))
   159  
   160  	for i := 7; i <= 20; i++ {
   161  		s := strconv.Itoa(i)
   162  		require.True(t, c.Contains(uint64(i)), i)
   163  		v, ok := c.Peek(uint64(i))
   164  		require.True(t, ok, i)
   165  		require.Equal(t, "V" + s, v)
   166  	}
   167  }
   168  
   169  func BenchmarkCore(b *testing.B) {
   170  	b.Run("fenced-16K", func(b *testing.B) {
   171  		benchmarkCore(b, newStrategy(1024, 1<<14, true, false))
   172  	})
   173  
   174  	b.Run("bare-16K", func(b *testing.B) {
   175  		benchmarkCore(b, newStrategy(1024, 1<<14, false, false))
   176  	})
   177  
   178  	b.Run("fenced-16M", func(b *testing.B) {
   179  		benchmarkCore(b, newStrategy(1024, 1<<24, true, false))
   180  	})
   181  
   182  	b.Run("bare-16M", func(b *testing.B) {
   183  		benchmarkCore(b, newStrategy(1024, 1<<24, false, false))
   184  	})
   185  }
   186  
   187  func benchmarkCore(b *testing.B, s Strategy) {
   188  	c := NewUintCache(s)
   189  	b.Run("put", func(b *testing.B) {
   190  		b.ReportAllocs()
   191  		for i := b.N; i > 0; i-- {
   192  			c.Put(uint64(i), "")
   193  		}
   194  	})
   195  
   196  	max := c.Occupied()
   197  	b.Run("put-dup", func(b *testing.B) {
   198  		b.ReportAllocs()
   199  		for i := b.N; i > 0; i-- {
   200  			c.Put(uint64(i%max), "")
   201  		}
   202  	})
   203  
   204  	b.Run("peek", func(b *testing.B) {
   205  		b.ReportAllocs()
   206  		for i := b.N; i > 0; i-- {
   207  			c.Peek(uint64(i%max))
   208  		}
   209  	})
   210  
   211  	b.Run("get", func(b *testing.B) {
   212  		b.ReportAllocs()
   213  		for i := b.N; i > 0; i-- {
   214  			c.Get(uint64(i%max))
   215  		}
   216  	})
   217  
   218  	b.Run("get-subset", func(b *testing.B) {
   219  		b.ReportAllocs()
   220  		for i := b.N; i > 0; i-- {
   221  			c.Get(uint64((i&0xFF)<<10)) // access a small (256) set of records across all
   222  		}
   223  	})
   224  }
   225  
   226  func BenchmarkFence(b *testing.B) {
   227  	b.Run("fenced", func(b *testing.B) {
   228  		benchmarkFence(b, newStrategy(32, 4, true, false))
   229  	})
   230  
   231  	b.Run("bare", func(b *testing.B) {
   232  		benchmarkFence(b, newStrategy(32, 4, false, false))
   233  	})
   234  }
   235  
   236  func benchmarkFence(b *testing.B, s Strategy) {
   237  	c := NewUintCache(s)
   238  	c.Put(0, "")
   239  	c.Put(1, "")
   240  
   241  	b.Run("get", func(b *testing.B) {
   242  		b.ReportAllocs()
   243  		for i := b.N; i > 0; i-- {
   244  			c.Get(uint64(i)&1)
   245  		}
   246  	})
   247  
   248  }