Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add freelru to gc overhead comparison #20

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 83 additions & 30 deletions caches_gc_overhead_comparison.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

"github.com/allegro/bigcache/v3"
"github.com/coocood/freecache"
"github.com/elastic/go-freelru"
"github.com/zeebo/xxh3"
)

var previousPause time.Duration
Expand Down Expand Up @@ -41,7 +43,7 @@ func main() {
fmt.Println("Number of repeats: ", repeat)
fmt.Println("Value size: ", valueSize)

var benchFunc func(entries, valueSize int)
var benchFunc func(kv *keyValueStore)

switch c {
case "freecache":
Expand All @@ -50,79 +52,130 @@ func main() {
benchFunc = bigCache
case "stdmap":
benchFunc = stdMap
case "freelru":
benchFunc = freeLRU
default:
fmt.Printf("unknown cache: %s", c)
os.Exit(1)
}

benchFunc(entries, valueSize)
kv := newKeyValueStore(entries, valueSize)

benchFunc(kv)
fmt.Println("GC pause for startup: ", gcPause())
for i := 0; i < repeat; i++ {
benchFunc(entries, valueSize)
benchFunc(kv)
}

fmt.Printf("GC pause for %s: %s\n", c, gcPause())
}

func stdMap(entries, valueSize int) {
func stdMap(kv *keyValueStore) {
mapCache := make(map[string][]byte)
for i := 0; i < entries; i++ {
key, val := generateKeyValue(i, valueSize)
mapCache[key] = val
for i := 0; i < kv.Size(); i++ {
mapCache[kv.Key(i)] = kv.Value(i)
}
}

func freeCache(entries, valueSize int) {
freeCache := freecache.NewCache(entries * 200) //allocate entries * 200 bytes
for i := 0; i < entries; i++ {
key, val := generateKeyValue(i, valueSize)
if err := freeCache.Set([]byte(key), val, 0); err != nil {
func freeCache(kv *keyValueStore) {
freeCache := freecache.NewCache(kv.Size() * 200) //allocate entries * 200 bytes
for i := 0; i < kv.Size(); i++ {
if err := freeCache.Set([]byte(kv.Key(i)), kv.Value(i), 0); err != nil {
fmt.Println("Error in set: ", err.Error())
}
}

firstKey, _ := generateKeyValue(1, valueSize)
v, err := freeCache.Get([]byte(firstKey))
checkFirstElement(valueSize, v, err)
v, err := freeCache.Get([]byte(kv.Key(1)))
checkFirstElement(kv.Value(1), v, err)

if freeCache.OverwriteCount() != 0 {
fmt.Println("Overwritten: ", freeCache.OverwriteCount())
}
}

func bigCache(entries, valueSize int) {
func freeLRU(kv *keyValueStore) {
// Using NewSynced() here to stay fair with concurrency.
// Using New() would be faster, but not thread-safe.
freeLRU, err := freelru.NewSynced[string, []byte](uint32(kv.Size()), hashString)
if err != nil {
fmt.Println("Failed to create freeLRU: ", err.Error())
return
}

for i := 0; i < kv.Size(); i++ {
freeLRU.Add(kv.Key(i), kv.Value(i))
}

v, ok := freeLRU.Get(kv.Key(1))
if !ok {
fmt.Println("First item not found")
return
}
checkFirstElement(kv.Value(1), v, nil)
}

func hashString(s string) uint32 {
return uint32(xxh3.HashString(s))
}

func bigCache(kv *keyValueStore) {
config := bigcache.Config{
Shards: 256,
LifeWindow: 100 * time.Minute,
MaxEntriesInWindow: entries,
MaxEntriesInWindow: kv.Size(),
MaxEntrySize: 200,
Verbose: true,
}

bigcache, _ := bigcache.NewBigCache(config)
for i := 0; i < entries; i++ {
key, val := generateKeyValue(i, valueSize)
bigcache.Set(key, val)
for i := 0; i < kv.Size(); i++ {
bigcache.Set(kv.Key(i), kv.Value(i))
}

firstKey, _ := generateKeyValue(1, valueSize)
v, err := bigcache.Get(firstKey)
checkFirstElement(valueSize, v, err)
v, err := bigcache.Get(kv.Key(1))
checkFirstElement(kv.Value(1), v, err)
}

func checkFirstElement(valueSize int, val []byte, err error) {
_, expectedVal := generateKeyValue(1, valueSize)
func checkFirstElement(expectedVal []byte, val []byte, err error) {
if err != nil {
fmt.Println("Error in get: ", err.Error())
} else if string(val) != string(expectedVal) {
fmt.Println("Wrong first element: ", string(val))
}
}

func generateKeyValue(index int, valSize int) (string, []byte) {
key := fmt.Sprintf("key-%010d", index)
fixedNumber := []byte(fmt.Sprintf("%010d", index))
val := append(make([]byte, valSize-10), fixedNumber...)
type keyValueStore struct {
valueSize int
keys []string
values []byte
}

func newKeyValueStore(entries int, valueSize int) *keyValueStore {
keys := make([]string, entries)
values := make([]byte, entries*valueSize)

for i := 0; i < entries; i++ {
keys[i] = fmt.Sprintf("key-%010d", i)
// Reuse the underlying data of key to generate the value without allocating more memory.
value := (([]byte)(keys[i]))[4:]
copy(values[(i+1)*valueSize-len(value):], value)
}

return &keyValueStore{
valueSize: valueSize,
keys: keys,
values: values,
}
}

func (store *keyValueStore) Size() int {
return len(store.keys)
}

func (store *keyValueStore) Key(index int) string {
return store.keys[index]
}

return key, val
func (store *keyValueStore) Value(index int) []byte {
return store.values[index*store.valueSize : (index+1)*store.valueSize]
}
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ go 1.19
require (
github.com/allegro/bigcache/v3 v3.1.0
github.com/coocood/freecache v1.2.4
github.com/elastic/go-freelru v0.9.0
github.com/orcaman/concurrent-map/v2 v2.0.1
github.com/zeebo/xxh3 v1.0.2
)

require github.com/cespare/xxhash/v2 v2.2.0 // indirect
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,12 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M=
github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
github.com/elastic/go-freelru v0.9.0 h1:s9K5Q4xBoQC96XogjymtKDYfcABkoyDUoSIG4vprywg=
github.com/elastic/go-freelru v0.9.0/go.mod h1:bSdWT4M0lW79K8QbX6XY2heQYSCqD7THoYf82pT/H3I=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
Loading