github.com/kpango/gache/v2@v2.0.8/gache_benchmark_test.go (about)

     1  package gache
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"os"
     7  	"runtime"
     8  	"runtime/debug"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  	"unsafe"
    15  )
    16  
    17  type DefaultMap struct {
    18  	mu   sync.RWMutex
    19  	data map[interface{}]interface{}
    20  }
    21  
    22  func NewDefault() *DefaultMap {
    23  	return &DefaultMap{
    24  		data: make(map[interface{}]interface{}),
    25  	}
    26  }
    27  
    28  func (m *DefaultMap) Get(key interface{}) (interface{}, bool) {
    29  	m.mu.RLock()
    30  	defer m.mu.RUnlock()
    31  	v, ok := m.data[key]
    32  	return v, ok
    33  }
    34  
    35  func (m *DefaultMap) Set(key, val interface{}) {
    36  	m.mu.Lock()
    37  	defer m.mu.Unlock()
    38  }
    39  
    40  var (
    41  	ttl time.Duration = 50 * time.Millisecond
    42  
    43  	parallelism = 10000
    44  
    45  	bigData      = map[string]string{}
    46  	bigDataLen   = 2 << 10
    47  	bigDataCount = 2 << 16
    48  
    49  	smallData = map[string]string{
    50  		"string": "aaaa",
    51  		"int":    "123",
    52  		"float":  "99.99",
    53  		"struct": "struct{}{}",
    54  	}
    55  )
    56  
    57  func init() {
    58  	for i := 0; i < bigDataCount; i++ {
    59  		bigData[randStr(bigDataLen)] = randStr(bigDataLen)
    60  	}
    61  }
    62  
    63  var randSrc = rand.NewSource(time.Now().UnixNano())
    64  
    65  const (
    66  	rs6Letters       = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    67  	rs6LetterIdxBits = 6
    68  	rs6LetterIdxMask = 1<<rs6LetterIdxBits - 1
    69  	rs6LetterIdxMax  = 63 / rs6LetterIdxBits
    70  )
    71  
    72  func randStr(n int) string {
    73  	b := make([]byte, n)
    74  	cache, remain := randSrc.Int63(), rs6LetterIdxMax
    75  	for i := n - 1; i >= 0; {
    76  		if remain == 0 {
    77  			cache, remain = randSrc.Int63(), rs6LetterIdxMax
    78  		}
    79  		idx := int(cache & rs6LetterIdxMask)
    80  		if idx < len(rs6Letters) {
    81  			b[i] = rs6Letters[idx]
    82  			i--
    83  		}
    84  		cache >>= rs6LetterIdxBits
    85  		remain--
    86  	}
    87  	return *(*string)(unsafe.Pointer(&b))
    88  }
    89  
    90  func benchmark(b *testing.B, data map[string]string,
    91  	t time.Duration,
    92  	set func(string, string, time.Duration),
    93  	get func(string),
    94  ) {
    95  	b.Helper()
    96  	b.SetParallelism(parallelism)
    97  	b.ReportAllocs()
    98  	b.ResetTimer()
    99  	b.RunParallel(func(pb *testing.PB) {
   100  		for pb.Next() {
   101  			for k, v := range data {
   102  				set(k, v, t)
   103  			}
   104  			for k := range data {
   105  				get(k)
   106  			}
   107  		}
   108  	})
   109  }
   110  
   111  func BenchmarkDefaultMapSetSmallDataNoTTL(b *testing.B) {
   112  	m := NewDefault()
   113  	benchmark(b, smallData, NoTTL,
   114  		func(k, v string, t time.Duration) { m.Set(k, v) },
   115  		func(k string) { m.Get(k) })
   116  }
   117  
   118  func BenchmarkDefaultMapSetBigDataNoTTL(b *testing.B) {
   119  	m := NewDefault()
   120  	benchmark(b, bigData, NoTTL,
   121  		func(k, v string, t time.Duration) { m.Set(k, v) },
   122  		func(k string) { m.Get(k) })
   123  }
   124  
   125  func BenchmarkSyncMapSetSmallDataNoTTL(b *testing.B) {
   126  	var m sync.Map
   127  	benchmark(b, smallData, NoTTL,
   128  		func(k, v string, t time.Duration) { m.Store(k, v) },
   129  		func(k string) { m.Load(k) })
   130  }
   131  
   132  func BenchmarkSyncMapSetBigDataNoTTL(b *testing.B) {
   133  	var m sync.Map
   134  	benchmark(b, bigData, NoTTL,
   135  		func(k, v string, t time.Duration) { m.Store(k, v) },
   136  		func(k string) { m.Load(k) })
   137  }
   138  
   139  func BenchmarkGacheSetSmallDataNoTTL(b *testing.B) {
   140  	g := New[string](
   141  		WithDefaultExpiration[string](NoTTL),
   142  	)
   143  	benchmark(b, smallData, NoTTL,
   144  		func(k, v string, t time.Duration) { g.Set(k, v) },
   145  		func(k string) { g.Get(k) })
   146  }
   147  
   148  func BenchmarkGacheSetSmallDataWithTTL(b *testing.B) {
   149  	g := New(
   150  		WithDefaultExpiration[string](ttl),
   151  	)
   152  	benchmark(b, smallData, ttl,
   153  		func(k, v string, t time.Duration) { g.SetWithExpire(k, v, t) },
   154  		func(k string) { g.Get(k) })
   155  }
   156  
   157  func BenchmarkGacheSetBigDataNoTTL(b *testing.B) {
   158  	g := New(
   159  		WithDefaultExpiration[string](NoTTL),
   160  	)
   161  	benchmark(b, bigData, NoTTL,
   162  		func(k, v string, t time.Duration) { g.Set(k, v) },
   163  		func(k string) { g.Get(k) })
   164  }
   165  
   166  func BenchmarkGacheSetBigDataWithTTL(b *testing.B) {
   167  	g := New(
   168  		WithDefaultExpiration[string](ttl),
   169  	)
   170  	benchmark(b, bigData, ttl,
   171  		func(k, v string, t time.Duration) { g.SetWithExpire(k, v, t) },
   172  		func(k string) { g.Get(k) })
   173  }
   174  
   175  func TestMain(m *testing.M) {
   176  	setup()
   177  	code := m.Run()
   178  	shutdown()
   179  	os.Exit(code)
   180  }
   181  
   182  func setup() {
   183  	debug.SetGCPercent(10)
   184  }
   185  
   186  func shutdown() {
   187  	PrintGCPause()
   188  	PrintMem()
   189  	PrintRate()
   190  }
   191  
   192  func BenchmarkHeavyMixedInt_gache(b *testing.B) {
   193  	gc := New[int]().SetDefaultExpire(10 * time.Second)
   194  	var wg sync.WaitGroup
   195  	for index := 0; index < 10000; index++ {
   196  		wg.Add(1)
   197  		go func() {
   198  			for i := 0; i < 8192; i++ {
   199  				gc.Set(Int64Key(int64(i)), i+1)
   200  			}
   201  			wg.Done()
   202  		}()
   203  		wg.Add(1)
   204  		go func() {
   205  			for i := 0; i < 8192; i++ {
   206  				gc.Get(Int64Key(int64(i)))
   207  			}
   208  			wg.Done()
   209  		}()
   210  	}
   211  	wg.Wait()
   212  
   213  	AddMem()
   214  }
   215  
   216  func BenchmarkPutInt_gache(b *testing.B) {
   217  	gc := New[int]().SetDefaultExpire(10 * time.Second)
   218  	// slen = 512
   219  	for i := 0; i < b.N; i++ {
   220  		gc.Set(Int64Key(int64(i)), i+1)
   221  	}
   222  }
   223  
   224  func BenchmarkGetInt_gache(b *testing.B) {
   225  	gc := New[string]().SetDefaultExpire(10 * time.Second)
   226  	// slen = 512
   227  	gc.Set("0", "0")
   228  	for i := 0; i < b.N; i++ {
   229  		gc.Get("0")
   230  	}
   231  }
   232  
   233  func BenchmarkPut1K_gache(b *testing.B) {
   234  	gc := New[[]byte]().SetDefaultExpire(10 * time.Second)
   235  	// slen = 512
   236  	for i := 0; i < b.N; i++ {
   237  		gc.Set(Int64Key(int64(i)), Data1K)
   238  	}
   239  }
   240  
   241  func BenchmarkPut1M_gache(b *testing.B) {
   242  	gc := New[[]byte]().SetDefaultExpire(10 * time.Second)
   243  	// slen = 512
   244  	for i := 0; i < b.N; i++ {
   245  		gc.Set(Int64Key(int64(i)), Data1M)
   246  	}
   247  }
   248  
   249  func BenchmarkPutTinyObject_gache(b *testing.B) {
   250  	gc := New[dummyData]().SetDefaultExpire(10 * time.Second)
   251  	// slen = 512
   252  	for i := 0; i < b.N; i++ {
   253  		gc.Set(Int64Key(int64(i)), dummyData{})
   254  	}
   255  }
   256  
   257  func BenchmarkChangeOutAllInt_gache(b *testing.B) {
   258  	gc := New[int]().SetDefaultExpire(10 * time.Second)
   259  	// slen = 512
   260  	for i := 0; i < b.N*1024; i++ {
   261  		gc.Set(Int64Key(int64(i)), i+1)
   262  	}
   263  }
   264  
   265  func BenchmarkHeavyReadInt_gache(b *testing.B) {
   266  	gc := New[int]().SetDefaultExpire(10 * time.Second)
   267  	GCPause()
   268  
   269  	// slen = 512
   270  	for i := 0; i < 1024; i++ {
   271  		gc.Set(Int64Key(int64(i)), i+1)
   272  	}
   273  	var wg sync.WaitGroup
   274  	for index := 0; index < 10000; index++ {
   275  		wg.Add(1)
   276  		go func() {
   277  			for i := 0; i < 1024; i++ {
   278  				gc.Get(Int64Key(int64(i)))
   279  			}
   280  			wg.Done()
   281  		}()
   282  	}
   283  	wg.Wait()
   284  
   285  	AddGCPause()
   286  }
   287  
   288  func BenchmarkHeavyWriteInt_gache(b *testing.B) {
   289  	gc := New[int]().SetDefaultExpire(10 * time.Second)
   290  	GCPause()
   291  
   292  	// slen = 512
   293  	var wg sync.WaitGroup
   294  	for index := 0; index < 10000; index++ {
   295  		start := index
   296  		wg.Add(1)
   297  		go func() {
   298  			for i := 0; i < 8192; i++ {
   299  				gc.Set(Int64Key(int64(i+start)), i+1)
   300  			}
   301  			wg.Done()
   302  		}()
   303  	}
   304  	wg.Wait()
   305  
   306  	AddGCPause()
   307  }
   308  
   309  func BenchmarkHeavyWrite1K_gache(b *testing.B) {
   310  	gc := New[[]byte]().SetDefaultExpire(10 * time.Second)
   311  	GCPause()
   312  
   313  	// slen = 512
   314  	var wg sync.WaitGroup
   315  	for index := 0; index < 10000; index++ {
   316  		start := index
   317  		wg.Add(1)
   318  		go func() {
   319  			for i := 0; i < 8192; i++ {
   320  				gc.Set(Int64Key(int64(i+start)), Data1K)
   321  			}
   322  			wg.Done()
   323  		}()
   324  	}
   325  	wg.Wait()
   326  
   327  	AddGCPause()
   328  }
   329  
   330  func Int64Key(d int64) string {
   331  	return strconv.FormatInt(d, 10)
   332  }
   333  
   334  func randomString(n int) []byte {
   335  	b := make([]byte, n)
   336  	for i := range b {
   337  		b[i] = byte(rand.Intn(26) + 'a')
   338  	}
   339  	return b
   340  }
   341  
   342  var (
   343  	Data1K = randomString(1024)
   344  	Data1M = randomString(1048576)
   345  )
   346  
   347  var previousPause time.Duration
   348  
   349  func GCPause() time.Duration {
   350  	runtime.GC()
   351  	var stats debug.GCStats
   352  	debug.ReadGCStats(&stats)
   353  	pause := stats.PauseTotal - previousPause
   354  	previousPause = stats.PauseTotal
   355  	return pause
   356  }
   357  
   358  var gcResult = make(map[string]time.Duration, 0)
   359  
   360  func AddGCPause() {
   361  	pc, _, _, _ := runtime.Caller(1)
   362  	name := strings.Replace(runtime.FuncForPC(pc).Name(), "_", "GC_", 1)
   363  	name = name[strings.Index(name, "Benchmark"):]
   364  	if _, ok := gcResult[name]; !ok {
   365  		gcResult[name] = GCPause()
   366  	}
   367  }
   368  
   369  func PrintGCPause() {
   370  	for k, v := range gcResult {
   371  		fmt.Printf("%s-1 1 %d ns/op\n", k, v)
   372  	}
   373  }
   374  
   375  func PrintMem() {
   376  	for k, v := range memResult {
   377  		fmt.Printf("%s-1 1 %d B\n", k, v)
   378  	}
   379  }
   380  
   381  var memResult = make(map[string]uint64, 0)
   382  
   383  func AddMem() {
   384  	var ms runtime.MemStats
   385  	runtime.ReadMemStats(&ms)
   386  	pc, _, _, _ := runtime.Caller(1)
   387  	name := strings.Replace(runtime.FuncForPC(pc).Name(), "_", "Mem_", 1)
   388  	name = name[strings.Index(name, "Benchmark"):]
   389  	if _, ok := memResult[name]; !ok {
   390  		memResult[name] = ms.Sys
   391  	}
   392  }
   393  
   394  var rateResult = make(map[string]float64, 0)
   395  
   396  func AddRate(r float64) {
   397  	var ms runtime.MemStats
   398  	runtime.ReadMemStats(&ms)
   399  	pc, _, _, _ := runtime.Caller(1)
   400  	name := runtime.FuncForPC(pc).Name()
   401  	name = name[strings.Index(name, "Benchmark"):]
   402  	if _, ok := rateResult[name]; !ok {
   403  		rateResult[name] = r
   404  	}
   405  }
   406  
   407  func PrintRate() {
   408  	for k, v := range rateResult {
   409  		fmt.Printf("%s-1 1 %.2f %%\n", k, 100.*v)
   410  	}
   411  }
   412  
   413  type dummyData struct {
   414  	Name    string
   415  	Age     int32
   416  	Gender  int32
   417  	Company string
   418  	Skills  []string
   419  }