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 benchmarks to v1 and v2 #13

Merged
merged 1 commit into from
Jul 30, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
working-directory: v2

- name: golangci-lint
uses: golangci/golangci-lint-action@v4
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --config ../.golangci.yml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
working-directory: v3

- name: golangci-lint
uses: golangci/golangci-lint-action@v4
uses: golangci/golangci-lint-action@v6
with:
version: latest
args: --config ../.golangci.yml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
go build -race

- name: golangci-lint
uses: golangci/golangci-lint-action@v4
uses: golangci/golangci-lint-action@v6
with:
version: latest

Expand Down
34 changes: 14 additions & 20 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
linters-settings:
govet:
check-shadowing: true
enable:
- shadow
gocyclo:
min-complexity: 15
misspell:
Expand All @@ -15,30 +16,23 @@ linters-settings:

linters:
enable:
- megacheck
- revive
- govet
- unconvert
- megacheck
- gas
- gocyclo
- dupl
- misspell
- typecheck
- ineffassign
- stylecheck
- gochecknoinits
- exportloopref
- gochecknoinits
- gocritic
- nakedret
- gocyclo
- gosec
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- prealloc
- revive
- staticcheck
- stylecheck
- typecheck
- unconvert
- unused
fast: false
disable-all: true

run:
output:
format: tab
skip-dirs:
- vendor
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,90 @@ func main() {
// value after expiration is found: false, value: <nil>
// Size: 1, Stats: {Hits:1 Misses:1 Added:2 Evicted:1} (50.0%)
}
```
```

### v3 performance improvements

v3 (and v2) are done using generics and 38-42% faster than v1 without them according to benchmarks.

<details>
<summary>v1</summary>

```
~/expirable-cache ❯ go test -bench=.
goos: darwin
goarch: arm64
pkg: github.com/go-pkgz/expirable-cache
BenchmarkLRU_Rand_NoExpire-8 4494738 272.4 ns/op
--- BENCH: BenchmarkLRU_Rand_NoExpire-8
cache_test.go:46: hit: 0 miss: 1 ratio: 0.000000
cache_test.go:46: hit: 1 miss: 99 ratio: 0.010000
cache_test.go:46: hit: 1352 miss: 8648 ratio: 0.135200
cache_test.go:46: hit: 248678 miss: 751322 ratio: 0.248678
cache_test.go:46: hit: 1121791 miss: 3372947 ratio: 0.249579
BenchmarkLRU_Freq_NoExpire-8 4612648 261.6 ns/op
--- BENCH: BenchmarkLRU_Freq_NoExpire-8
cache_test.go:74: hit: 1 miss: 0 ratio: 1.000000
cache_test.go:74: hit: 100 miss: 0 ratio: 1.000000
cache_test.go:74: hit: 9825 miss: 175 ratio: 0.982500
cache_test.go:74: hit: 312345 miss: 687655 ratio: 0.312345
cache_test.go:74: hit: 1414620 miss: 3198028 ratio: 0.306683
BenchmarkLRU_Rand_WithExpire-8 4109704 286.5 ns/op
--- BENCH: BenchmarkLRU_Rand_WithExpire-8
cache_test.go:99: hit: 0 miss: 1 ratio: 0.000000
cache_test.go:99: hit: 0 miss: 100 ratio: 0.000000
cache_test.go:99: hit: 1304 miss: 8696 ratio: 0.130400
cache_test.go:99: hit: 248310 miss: 751690 ratio: 0.248310
cache_test.go:99: hit: 1027317 miss: 3082387 ratio: 0.249973
BenchmarkLRU_Freq_WithExpire-8 4341217 279.6 ns/op
--- BENCH: BenchmarkLRU_Freq_WithExpire-8
cache_test.go:127: hit: 1 miss: 0 ratio: 1.000000
cache_test.go:127: hit: 100 miss: 0 ratio: 1.000000
cache_test.go:127: hit: 9868 miss: 132 ratio: 0.986800
cache_test.go:127: hit: 38221 miss: 961779 ratio: 0.038221
cache_test.go:127: hit: 37296 miss: 4303921 ratio: 0.008591
PASS
ok github.com/go-pkgz/expirable-cache 18.307s
```
</details>

<details>
<summary>v3</summary>

```
~/Desktop/expirable-cache/v3 master !2 ❯ go test -bench=.
goos: darwin
goarch: arm64
pkg: github.com/go-pkgz/expirable-cache/v3
BenchmarkLRU_Rand_NoExpire-8 7556680 158.1 ns/op
--- BENCH: BenchmarkLRU_Rand_NoExpire-8
cache_test.go:47: hit: 0 miss: 1 ratio: 0.000000
cache_test.go:47: hit: 0 miss: 100 ratio: 0.000000
cache_test.go:47: hit: 1409 miss: 8591 ratio: 0.140900
cache_test.go:47: hit: 249063 miss: 750937 ratio: 0.249063
cache_test.go:47: hit: 1887563 miss: 5669117 ratio: 0.249787
BenchmarkLRU_Freq_NoExpire-8 7876738 150.9 ns/op
--- BENCH: BenchmarkLRU_Freq_NoExpire-8
cache_test.go:75: hit: 1 miss: 0 ratio: 1.000000
cache_test.go:75: hit: 100 miss: 0 ratio: 1.000000
cache_test.go:75: hit: 9850 miss: 150 ratio: 0.985000
cache_test.go:75: hit: 310888 miss: 689112 ratio: 0.310888
cache_test.go:75: hit: 2413312 miss: 5463426 ratio: 0.306385
BenchmarkLRU_Rand_WithExpire-8 6822362 175.3 ns/op
--- BENCH: BenchmarkLRU_Rand_WithExpire-8
cache_test.go:100: hit: 0 miss: 1 ratio: 0.000000
cache_test.go:100: hit: 0 miss: 100 ratio: 0.000000
cache_test.go:100: hit: 1326 miss: 8674 ratio: 0.132600
cache_test.go:100: hit: 248508 miss: 751492 ratio: 0.248508
cache_test.go:100: hit: 1704172 miss: 5118190 ratio: 0.249792
BenchmarkLRU_Freq_WithExpire-8 7098261 168.1 ns/op
--- BENCH: BenchmarkLRU_Freq_WithExpire-8
cache_test.go:128: hit: 1 miss: 0 ratio: 1.000000
cache_test.go:128: hit: 100 miss: 0 ratio: 1.000000
cache_test.go:128: hit: 9842 miss: 158 ratio: 0.984200
cache_test.go:128: hit: 90167 miss: 909833 ratio: 0.090167
cache_test.go:128: hit: 90421 miss: 7007840 ratio: 0.012738
PASS
ok github.com/go-pkgz/expirable-cache/v3 24.315s
```
</details>
118 changes: 118 additions & 0 deletions cache_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,132 @@
package cache

import (
"crypto/rand"
"fmt"
"math"
"math/big"
"strconv"
"sync"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func getRand(tb testing.TB) int64 {
out, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
tb.Fatal(err)
}
return out.Int64()
}

func BenchmarkLRU_Rand_NoExpire(b *testing.B) {
l, _ := NewCache(MaxKeys(8192), LRU())

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = getRand(b) % 32768
}

b.ResetTimer()

var hit, miss int
for i := 0; i < 2*b.N; i++ {
if i%2 == 0 {
l.Set(strconv.Itoa(int(trace[i])), trace[i], 0)
} else {
if _, ok := l.Get(strconv.Itoa(int(trace[i]))); ok {
hit++
} else {
miss++
}
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(hit+miss))
}

func BenchmarkLRU_Freq_NoExpire(b *testing.B) {
l, _ := NewCache(MaxKeys(8192), LRU())

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = getRand(b) % 16384
} else {
trace[i] = getRand(b) % 32768
}
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
l.Set(strconv.Itoa(int(trace[i])), trace[i], 0)
}
var hit, miss int
for i := 0; i < b.N; i++ {
if _, ok := l.Get(strconv.Itoa(int(trace[i]))); ok {
hit++
} else {
miss++
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(hit+miss))
}

func BenchmarkLRU_Rand_WithExpire(b *testing.B) {
l, _ := NewCache(MaxKeys(8192), LRU(), TTL(time.Millisecond*10))

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
trace[i] = getRand(b) % 32768
}

b.ResetTimer()

var hit, miss int
for i := 0; i < 2*b.N; i++ {
if i%2 == 0 {
l.Set(strconv.Itoa(int(trace[i])), trace[i], 0)
} else {
if _, ok := l.Get(strconv.Itoa(int(trace[i]))); ok {
hit++
} else {
miss++
}
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(hit+miss))
}

func BenchmarkLRU_Freq_WithExpire(b *testing.B) {
l, _ := NewCache(MaxKeys(8192), LRU(), TTL(time.Millisecond*10))

trace := make([]int64, b.N*2)
for i := 0; i < b.N*2; i++ {
if i%2 == 0 {
trace[i] = getRand(b) % 16384
} else {
trace[i] = getRand(b) % 32768
}
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
l.Set(strconv.Itoa(int(trace[i])), trace[i], 0)
}
var hit, miss int
for i := 0; i < b.N; i++ {
if _, ok := l.Get(strconv.Itoa(int(trace[i]))); ok {
hit++
} else {
miss++
}
}
b.Logf("hit: %d miss: %d ratio: %f", hit, miss, float64(hit)/float64(hit+miss))
}

func TestCacheNoPurge(t *testing.T) {
lc, err := NewCache()
assert.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ func OnEvicted(fn func(key string, value interface{})) Option {

// MaxKeys functional option defines how many keys to keep.
// By default it is 0, which means unlimited.
func MaxKeys(max int) Option {
func MaxKeys(maximum int) Option {
return func(lc *cacheImpl) error {
lc.maxKeys = max
lc.maxKeys = maximum
return nil
}
}
Expand Down
Loading
Loading