From a45ed98559a8a57870f724b765bb47d7126af96b Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 10:45:30 -0500 Subject: [PATCH 01/17] Add benchmarks that use expiring items (time.Now calls) and rename BenchmarkDeleteExpired to BenchmarkDeleteExpiredLoop for clarity --- cache_test.go | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/cache_test.go b/cache_test.go index f998d43..2ff220e 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1425,9 +1425,17 @@ func TestSerializeUnserializable(t *testing.T) { } } -func BenchmarkCacheGet(b *testing.B) { +func BenchmarkCacheGetExpiring(b *testing.B) { + benchmarkCacheGet(b, 5 * time.Minute) +} + +func BenchmarkCacheGetNotExpiring(b *testing.B) { + benchmarkCacheGet(b, NoExpiration) +} + +func benchmarkCacheGet(b *testing.B, exp time.Duration) { b.StopTimer() - tc := New(DefaultExpiration, 0) + tc := New(exp, 0) tc.Set("foo", "bar", DefaultExpiration) b.StartTimer() for i := 0; i < b.N; i++ { @@ -1449,9 +1457,17 @@ func BenchmarkRWMutexMapGet(b *testing.B) { } } -func BenchmarkCacheGetConcurrent(b *testing.B) { +func BenchmarkCacheGetConcurrentExpiring(b *testing.B) { + benchmarkCacheGetConcurrent(b, 5 * time.Minute) +} + +func BenchmarkCacheGetConcurrentNotExpiring(b *testing.B) { + benchmarkCacheGetConcurrent(b, NoExpiration) +} + +func benchmarkCacheGetConcurrent(b *testing.B, exp time.Duration) { b.StopTimer() - tc := New(DefaultExpiration, 0) + tc := New(exp, 0) tc.Set("foo", "bar", DefaultExpiration) wg := new(sync.WaitGroup) workers := runtime.NumCPU() @@ -1493,13 +1509,21 @@ func BenchmarkRWMutexMapGetConcurrent(b *testing.B) { wg.Wait() } -func BenchmarkCacheGetManyConcurrent(b *testing.B) { +func BenchmarkCacheGetManyConcurrentExpiring(b *testing.B) { + benchmarkCacheGetManyConcurrent(b, 5 * time.Minute) +} + +func BenchmarkCacheGetManyConcurrentNotExpiring(b *testing.B) { + benchmarkCacheGetManyConcurrent(b, NoExpiration) +} + +func benchmarkCacheGetManyConcurrent(b *testing.B, exp time.Duration) { // This is the same as BenchmarkCacheGetConcurrent, but its result // can be compared against BenchmarkShardedCacheGetManyConcurrent // in sharded_test.go. b.StopTimer() n := 10000 - tc := New(DefaultExpiration, 0) + tc := New(exp, 0) keys := make([]string, n) for i := 0; i < n; i++ { k := "foo" + strconv.Itoa(n) @@ -1521,9 +1545,17 @@ func BenchmarkCacheGetManyConcurrent(b *testing.B) { wg.Wait() } -func BenchmarkCacheSet(b *testing.B) { +func BenchmarkCacheSetExpiring(b *testing.B) { + benchmarkCacheSet(b, 5 * time.Minute) +} + +func BenchmarkCacheSetNotExpiring(b *testing.B) { + benchmarkCacheSet(b, NoExpiration) +} + +func benchmarkCacheSet(b *testing.B, exp time.Duration) { b.StopTimer() - tc := New(DefaultExpiration, 0) + tc := New(exp, 0) b.StartTimer() for i := 0; i < b.N; i++ { tc.Set("foo", "bar", DefaultExpiration) @@ -1602,7 +1634,7 @@ func BenchmarkIncrementInt(b *testing.B) { } } -func BenchmarkDeleteExpired(b *testing.B) { +func BenchmarkDeleteExpiredLoop(b *testing.B) { b.StopTimer() tc := New(5 * time.Minute, 0) tc.mu.Lock() From 4e0d34ef0010c94c55fbdc0ef08ea751979e7ed9 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 13:39:27 -0500 Subject: [PATCH 02/17] Only get the current time once in the DeleteExpired loop --- cache.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cache.go b/cache.go index 61040b8..2599f1a 100644 --- a/cache.go +++ b/cache.go @@ -17,12 +17,16 @@ type Item struct { Expiration time.Time } -// Returns true if the item has expired. -func (item Item) Expired() bool { +func (item Item) expired(now time.Time) bool { if item.Expiration == emptyTime { return false } - return item.Expiration.Before(time.Now()) + return item.Expiration.Before(now) +} + +// Returns true if the item has expired. +func (item Item) Expired() bool { + return item.expired(time.Now()) } const ( @@ -868,9 +872,10 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { var evictedItems []keyAndValue + now := time.Now() c.mu.Lock() for k, v := range c.items { - if v.Expired() { + if v.expired(now) { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) From 31c7be0bed73008bb6b43fc60c23711455d66614 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 13:50:17 -0500 Subject: [PATCH 03/17] 'Inline' Get and Expired --- cache.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cache.go b/cache.go index 2599f1a..a94bbd0 100644 --- a/cache.go +++ b/cache.go @@ -26,7 +26,11 @@ func (item Item) expired(now time.Time) bool { // Returns true if the item has expired. func (item Item) Expired() bool { - return item.expired(time.Now()) + // "Inlining" of expired + if item.Expiration == emptyTime { + return false + } + return item.Expiration.Before(time.Now()) } const ( @@ -108,9 +112,14 @@ func (c *cache) Replace(k string, x interface{}, d time.Duration) error { // whether the key was found. func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() - x, found := c.get(k) + // "Inlining" of get + item, found := c.items[k] + if !found || item.Expired() { + c.mu.RUnlock() + return nil, false + } c.mu.RUnlock() - return x, found + return item.Object, found } func (c *cache) get(k string) (interface{}, bool) { From eb4f9f6b2f44f94b24f25173e58ffb8fa5c4f37e Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 13:54:01 -0500 Subject: [PATCH 04/17] Use UnixNano int64s instead of Time --- cache.go | 18 +++++++++--------- cache_test.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cache.go b/cache.go index a94bbd0..5839ac1 100644 --- a/cache.go +++ b/cache.go @@ -14,23 +14,23 @@ var emptyTime = time.Time{} type Item struct { Object interface{} - Expiration time.Time + Expiration int64 } -func (item Item) expired(now time.Time) bool { - if item.Expiration == emptyTime { +func (item Item) expired(now int64) bool { + if item.Expiration == 0 { return false } - return item.Expiration.Before(now) + return now > item.Expiration } // Returns true if the item has expired. func (item Item) Expired() bool { // "Inlining" of expired - if item.Expiration == emptyTime { + if item.Expiration == 0 { return false } - return item.Expiration.Before(time.Now()) + return time.Now().UnixNano() > item.Expiration } const ( @@ -67,12 +67,12 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { } func (c *cache) set(k string, x interface{}, d time.Duration) { - e := emptyTime + var e int64 if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 { - e = time.Now().Add(d) + e = time.Now().Add(d).UnixNano() } c.items[k] = Item{ Object: x, @@ -881,7 +881,7 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { var evictedItems []keyAndValue - now := time.Now() + now := time.Now().UnixNano() c.mu.Lock() for k, v := range c.items { if v.expired(now) { diff --git a/cache_test.go b/cache_test.go index 2ff220e..f604794 100644 --- a/cache_test.go +++ b/cache_test.go @@ -110,11 +110,11 @@ func TestNewFrom(t *testing.T) { m := map[string]Item{ "a": Item{ Object: 1, - Expiration: emptyTime, + Expiration: 0, }, "b": Item{ Object: 2, - Expiration: emptyTime, + Expiration: 0, }, } tc := NewFrom(DefaultExpiration, 0, m) From 8084bd02b5d65f4bb74b5080254ad08faf15b336 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 14:12:45 -0500 Subject: [PATCH 05/17] Inline expiration checks manually for performance --- cache.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cache.go b/cache.go index 5839ac1..1a6c4e2 100644 --- a/cache.go +++ b/cache.go @@ -112,9 +112,9 @@ func (c *cache) Replace(k string, x interface{}, d time.Duration) error { // whether the key was found. func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() - // "Inlining" of get + // "Inlining" of get and expired item, found := c.items[k] - if !found || item.Expired() { + if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { c.mu.RUnlock() return nil, false } @@ -124,7 +124,8 @@ func (c *cache) Get(k string) (interface{}, bool) { func (c *cache) get(k string) (interface{}, bool) { item, found := c.items[k] - if !found || item.Expired() { + // "Inlining" of expired + if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { return nil, false } return item.Object, true @@ -884,7 +885,8 @@ func (c *cache) DeleteExpired() { now := time.Now().UnixNano() c.mu.Lock() for k, v := range c.items { - if v.expired(now) { + // "Inlining" of expired + if v.Expiration > 0 && now > v.Expiration { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) From 1924ec3baf9c7cb78662282fbec44fbb7f3d6d86 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 14:14:52 -0500 Subject: [PATCH 06/17] Remove expired() since it's no longer used (because of the inlining) --- cache.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cache.go b/cache.go index 1a6c4e2..cea90c5 100644 --- a/cache.go +++ b/cache.go @@ -17,16 +17,8 @@ type Item struct { Expiration int64 } -func (item Item) expired(now int64) bool { - if item.Expiration == 0 { - return false - } - return now > item.Expiration -} - // Returns true if the item has expired. func (item Item) Expired() bool { - // "Inlining" of expired if item.Expiration == 0 { return false } From 01842a547caf6a5d39d1db580f08923b840ea7fe Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 14:47:22 -0500 Subject: [PATCH 07/17] Use timevals --- cache.go | 44 +++++++++++++++++++++++++++++++------------- cache_test.go | 5 +++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/cache.go b/cache.go index cea90c5..b9bf974 100644 --- a/cache.go +++ b/cache.go @@ -7,22 +7,23 @@ import ( "os" "runtime" "sync" + "syscall" "time" ) -var emptyTime = time.Time{} - type Item struct { Object interface{} - Expiration int64 + Expiration syscall.Timeval } // Returns true if the item has expired. func (item Item) Expired() bool { - if item.Expiration == 0 { + if item.Expiration.Sec == 0 { return false } - return time.Now().UnixNano() > item.Expiration + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + return tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) } const ( @@ -59,12 +60,12 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { } func (c *cache) set(k string, x interface{}, d time.Duration) { - var e int64 + var e syscall.Timeval if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 { - e = time.Now().Add(d).UnixNano() + e = syscall.NsecToTimeval(time.Now().Add(d).UnixNano()) } c.items[k] = Item{ Object: x, @@ -106,20 +107,36 @@ func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() // "Inlining" of get and expired item, found := c.items[k] - if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { + if !found { c.mu.RUnlock() return nil, false } + if item.Expiration.Sec > 0 { + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + c.mu.RUnlock() + return nil, false + } + } c.mu.RUnlock() - return item.Object, found + return item.Object, true } func (c *cache) get(k string) (interface{}, bool) { item, found := c.items[k] - // "Inlining" of expired - if !found || (item.Expiration > 0 && time.Now().UnixNano() > item.Expiration) { + if !found { return nil, false } + // "Inlining" of Expired + if item.Expiration.Sec > 0 { + var tv syscall.Timeval + syscall.Gettimeofday(&tv) + if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + c.mu.RUnlock() + return nil, false + } + } return item.Object, true } @@ -874,11 +891,12 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { var evictedItems []keyAndValue - now := time.Now().UnixNano() + var now syscall.Timeval + syscall.Gettimeofday(&now) c.mu.Lock() for k, v := range c.items { // "Inlining" of expired - if v.Expiration > 0 && now > v.Expiration { + if v.Expiration.Sec > 0 && (now.Sec > v.Expiration.Sec || (now.Sec == v.Expiration.Sec && now.Usec > v.Expiration.Usec)) { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) diff --git a/cache_test.go b/cache_test.go index f604794..62b2854 100644 --- a/cache_test.go +++ b/cache_test.go @@ -6,6 +6,7 @@ import ( "runtime" "strconv" "sync" + "syscall" "testing" "time" ) @@ -110,11 +111,11 @@ func TestNewFrom(t *testing.T) { m := map[string]Item{ "a": Item{ Object: 1, - Expiration: 0, + Expiration: syscall.Timeval{}, }, "b": Item{ Object: 2, - Expiration: 0, + Expiration: syscall.Timeval{}, }, } tc := NewFrom(DefaultExpiration, 0, m) From 2f60853f802f7846ab59906612e15fcde066e217 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 14:49:18 -0500 Subject: [PATCH 08/17] No need for emptyTime anymore --- cache.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cache.go b/cache.go index cea90c5..e934361 100644 --- a/cache.go +++ b/cache.go @@ -10,8 +10,6 @@ import ( "time" ) -var emptyTime = time.Time{} - type Item struct { Object interface{} Expiration int64 From 2f0c74ebb8a6bd87a32958cdfff1124e527d4718 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 15:02:02 -0500 Subject: [PATCH 09/17] Use intermediary timevals --- cache.go | 31 +++++++++++++++++-------------- cache_test.go | 5 ++--- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cache.go b/cache.go index b9bf974..a573611 100644 --- a/cache.go +++ b/cache.go @@ -13,17 +13,17 @@ import ( type Item struct { Object interface{} - Expiration syscall.Timeval + Expiration int64 } // Returns true if the item has expired. func (item Item) Expired() bool { - if item.Expiration.Sec == 0 { + if item.Expiration == 0 { return false } var tv syscall.Timeval syscall.Gettimeofday(&tv) - return tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) + return tv.Nano() > item.Expiration } const ( @@ -60,12 +60,12 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) { } func (c *cache) set(k string, x interface{}, d time.Duration) { - var e syscall.Timeval + var e int64 if d == DefaultExpiration { d = c.defaultExpiration } if d > 0 { - e = syscall.NsecToTimeval(time.Now().Add(d).UnixNano()) + e = time.Now().Add(d).UnixNano() } c.items[k] = Item{ Object: x, @@ -105,16 +105,16 @@ func (c *cache) Replace(k string, x interface{}, d time.Duration) error { // whether the key was found. func (c *cache) Get(k string) (interface{}, bool) { c.mu.RLock() - // "Inlining" of get and expired + // "Inlining" of get and Expired item, found := c.items[k] if !found { c.mu.RUnlock() return nil, false } - if item.Expiration.Sec > 0 { + if item.Expiration > 0 { var tv syscall.Timeval syscall.Gettimeofday(&tv) - if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + if tv.Nano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -129,10 +129,10 @@ func (c *cache) get(k string) (interface{}, bool) { return nil, false } // "Inlining" of Expired - if item.Expiration.Sec > 0 { + if item.Expiration > 0 { var tv syscall.Timeval syscall.Gettimeofday(&tv) - if tv.Sec > item.Expiration.Sec || (tv.Sec == item.Expiration.Sec && tv.Usec > item.Expiration.Usec) { + if tv.Nano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -890,13 +890,16 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { - var evictedItems []keyAndValue - var now syscall.Timeval - syscall.Gettimeofday(&now) + var ( + evictedItems []keyAndValue + tv syscall.Timeval + ) + syscall.Gettimeofday(&tv) + now := tv.Nano() c.mu.Lock() for k, v := range c.items { // "Inlining" of expired - if v.Expiration.Sec > 0 && (now.Sec > v.Expiration.Sec || (now.Sec == v.Expiration.Sec && now.Usec > v.Expiration.Usec)) { + if v.Expiration > 0 && now > v.Expiration { ov, evicted := c.delete(k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) diff --git a/cache_test.go b/cache_test.go index 62b2854..f604794 100644 --- a/cache_test.go +++ b/cache_test.go @@ -6,7 +6,6 @@ import ( "runtime" "strconv" "sync" - "syscall" "testing" "time" ) @@ -111,11 +110,11 @@ func TestNewFrom(t *testing.T) { m := map[string]Item{ "a": Item{ Object: 1, - Expiration: syscall.Timeval{}, + Expiration: 0, }, "b": Item{ Object: 2, - Expiration: syscall.Timeval{}, + Expiration: 0, }, } tc := NewFrom(DefaultExpiration, 0, m) From afadf13f9f1c09f0c5e1b080662aa93cc12c5141 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 15:12:19 -0500 Subject: [PATCH 10/17] Back to UnixNano(), syscall dependency isn't worth a few nanoseconds better performance --- cache.go | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/cache.go b/cache.go index a573611..4f44ba2 100644 --- a/cache.go +++ b/cache.go @@ -7,7 +7,6 @@ import ( "os" "runtime" "sync" - "syscall" "time" ) @@ -21,9 +20,7 @@ func (item Item) Expired() bool { if item.Expiration == 0 { return false } - var tv syscall.Timeval - syscall.Gettimeofday(&tv) - return tv.Nano() > item.Expiration + return time.Now().UnixNano() > item.Expiration } const ( @@ -112,9 +109,7 @@ func (c *cache) Get(k string) (interface{}, bool) { return nil, false } if item.Expiration > 0 { - var tv syscall.Timeval - syscall.Gettimeofday(&tv) - if tv.Nano() > item.Expiration { + if time.Now().UnixNano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -130,9 +125,7 @@ func (c *cache) get(k string) (interface{}, bool) { } // "Inlining" of Expired if item.Expiration > 0 { - var tv syscall.Timeval - syscall.Gettimeofday(&tv) - if tv.Nano() > item.Expiration { + if time.Now().UnixNano() > item.Expiration { c.mu.RUnlock() return nil, false } @@ -890,12 +883,8 @@ type keyAndValue struct { // Delete all expired items from the cache. func (c *cache) DeleteExpired() { - var ( - evictedItems []keyAndValue - tv syscall.Timeval - ) - syscall.Gettimeofday(&tv) - now := tv.Nano() + var evictedItems []keyAndValue + now := time.Now().UnixNano() c.mu.Lock() for k, v := range c.items { // "Inlining" of expired From 9fc6f9c73f5599519846febf41781f43e2b9efca Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 16:04:49 -0500 Subject: [PATCH 11/17] Add expiring/notexpiring sharded cache benchmarks --- sharded_test.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/sharded_test.go b/sharded_test.go index 6ef5eb3..51088d9 100644 --- a/sharded_test.go +++ b/sharded_test.go @@ -4,6 +4,7 @@ import ( "strconv" "sync" "testing" + "time" ) // func TestDjb33(t *testing.T) { @@ -32,9 +33,17 @@ func TestShardedCache(t *testing.T) { } } -func BenchmarkShardedCacheGet(b *testing.B) { +func BenchmarkShardedCacheGetExpiring(b *testing.B) { + benchmarkShardedCacheGet(b, 5 * time.Minute) +} + +func BenchmarkShardedCacheGetNotExpiring(b *testing.B) { + benchmarkShardedCacheGet(b, NoExpiration) +} + +func benchmarkShardedCacheGet(b *testing.B, exp time.Duration) { b.StopTimer() - tc := unexportedNewSharded(DefaultExpiration, 0, 10) + tc := unexportedNewSharded(exp, 0, 10) tc.Set("foobarba", "zquux", DefaultExpiration) b.StartTimer() for i := 0; i < b.N; i++ { @@ -42,10 +51,18 @@ func BenchmarkShardedCacheGet(b *testing.B) { } } -func BenchmarkShardedCacheGetManyConcurrent(b *testing.B) { +func BenchmarkShardedCacheGetManyConcurrentExpiring(b *testing.B) { + benchmarkShardedCacheGetManyConcurrent(b, 5 * time.Minute) +} + +func BenchmarkShardedCacheGetManyConcurrentNotExpiring(b *testing.B) { + benchmarkShardedCacheGetManyConcurrent(b, NoExpiration) +} + +func benchmarkShardedCacheGetManyConcurrent(b *testing.B, exp time.Duration) { b.StopTimer() n := 10000 - tsc := unexportedNewSharded(DefaultExpiration, 0, 20) + tsc := unexportedNewSharded(exp, 0, 20) keys := make([]string, n) for i := 0; i < n; i++ { k := "foo" + strconv.Itoa(n) From 7c1e7f5829f0aef6c8d26dcdc76871584e9ab62f Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 16:04:57 -0500 Subject: [PATCH 12/17] go fmt --- cache_test.go | 10 +++++----- sharded_test.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cache_test.go b/cache_test.go index f604794..5a3b396 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1426,7 +1426,7 @@ func TestSerializeUnserializable(t *testing.T) { } func BenchmarkCacheGetExpiring(b *testing.B) { - benchmarkCacheGet(b, 5 * time.Minute) + benchmarkCacheGet(b, 5*time.Minute) } func BenchmarkCacheGetNotExpiring(b *testing.B) { @@ -1458,7 +1458,7 @@ func BenchmarkRWMutexMapGet(b *testing.B) { } func BenchmarkCacheGetConcurrentExpiring(b *testing.B) { - benchmarkCacheGetConcurrent(b, 5 * time.Minute) + benchmarkCacheGetConcurrent(b, 5*time.Minute) } func BenchmarkCacheGetConcurrentNotExpiring(b *testing.B) { @@ -1510,7 +1510,7 @@ func BenchmarkRWMutexMapGetConcurrent(b *testing.B) { } func BenchmarkCacheGetManyConcurrentExpiring(b *testing.B) { - benchmarkCacheGetManyConcurrent(b, 5 * time.Minute) + benchmarkCacheGetManyConcurrent(b, 5*time.Minute) } func BenchmarkCacheGetManyConcurrentNotExpiring(b *testing.B) { @@ -1546,7 +1546,7 @@ func benchmarkCacheGetManyConcurrent(b *testing.B, exp time.Duration) { } func BenchmarkCacheSetExpiring(b *testing.B) { - benchmarkCacheSet(b, 5 * time.Minute) + benchmarkCacheSet(b, 5*time.Minute) } func BenchmarkCacheSetNotExpiring(b *testing.B) { @@ -1636,7 +1636,7 @@ func BenchmarkIncrementInt(b *testing.B) { func BenchmarkDeleteExpiredLoop(b *testing.B) { b.StopTimer() - tc := New(5 * time.Minute, 0) + tc := New(5*time.Minute, 0) tc.mu.Lock() for i := 0; i < 100000; i++ { tc.set(strconv.Itoa(i), "bar", DefaultExpiration) diff --git a/sharded_test.go b/sharded_test.go index 51088d9..aef8597 100644 --- a/sharded_test.go +++ b/sharded_test.go @@ -34,7 +34,7 @@ func TestShardedCache(t *testing.T) { } func BenchmarkShardedCacheGetExpiring(b *testing.B) { - benchmarkShardedCacheGet(b, 5 * time.Minute) + benchmarkShardedCacheGet(b, 5*time.Minute) } func BenchmarkShardedCacheGetNotExpiring(b *testing.B) { @@ -52,7 +52,7 @@ func benchmarkShardedCacheGet(b *testing.B, exp time.Duration) { } func BenchmarkShardedCacheGetManyConcurrentExpiring(b *testing.B) { - benchmarkShardedCacheGetManyConcurrent(b, 5 * time.Minute) + benchmarkShardedCacheGetManyConcurrent(b, 5*time.Minute) } func BenchmarkShardedCacheGetManyConcurrentNotExpiring(b *testing.B) { From 76f1250a65318992f63b2fc75baafbe62e63a7a6 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Mon, 30 Nov 2015 16:18:49 -0500 Subject: [PATCH 13/17] Make OnEvicted() a little faster --- cache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.go b/cache.go index 4f44ba2..d58c603 100644 --- a/cache.go +++ b/cache.go @@ -906,8 +906,8 @@ func (c *cache) DeleteExpired() { // not when it is overwritten.) Set to nil to disable. func (c *cache) OnEvicted(f func(string, interface{})) { c.mu.Lock() - defer c.mu.Unlock() c.onEvicted = f + c.mu.Unlock() } // Write the cache's items (using Gob) to an io.Writer. From 63991427871ebd11ba924b3747c6bf129c72ee1e Mon Sep 17 00:00:00 2001 From: Giuseppe Date: Tue, 1 Dec 2015 09:12:35 +0100 Subject: [PATCH 14/17] added large cache test --- cache_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cache_test.go b/cache_test.go index f998d43..fd9ccb5 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1615,3 +1615,18 @@ func BenchmarkDeleteExpired(b *testing.B) { tc.DeleteExpired() } } + +func BenchmarkLargeCache(b *testing.B) { + b.StopTimer() + tc := New(200 * time.Millisecond, 50 * time.Millisecond) + //tc.mu.Lock() + for i := 0; i < 100000; i++ { + tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) + } + //tc.mu.Unlock() + tc.DeleteExpired() + b.StartTimer() + for i := 100000; i <100000 + b.N; i++ { + tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) + } +} From d461c5d2dd1e09fecf8be90a136289fadebbeef1 Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Tue, 1 Dec 2015 11:08:43 -0500 Subject: [PATCH 15/17] 'Inline' set in Set, and do time checks before the lock --- cache.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cache.go b/cache.go index d58c603..ff2c1e2 100644 --- a/cache.go +++ b/cache.go @@ -49,8 +49,19 @@ type cache struct { // (DefaultExpiration), the cache's default expiration time is used. If it is -1 // (NoExpiration), the item never expires. func (c *cache) Set(k string, x interface{}, d time.Duration) { + // "Inlining" of set + var e int64 + if d == DefaultExpiration { + d = c.defaultExpiration + } + if d > 0 { + e = time.Now().Add(d).UnixNano() + } c.mu.Lock() - c.set(k, x, d) + c.items[k] = Item{ + Object: x, + Expiration: e, + } // TODO: Calls to mu.Unlock are currently not deferred because defer // adds ~200 ns (as of go1.) c.mu.Unlock() From 66bf7b7a4586ad87161abca30cd43c2ecbc9b1cb Mon Sep 17 00:00:00 2001 From: Patrick Mylund Nielsen Date: Tue, 1 Dec 2015 11:18:46 -0500 Subject: [PATCH 16/17] Update README to point to new repository URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92e8a74..9cc4cc4 100644 --- a/README.md +++ b/README.md @@ -103,4 +103,4 @@ one) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats ### Reference -`godoc` or [http://godoc.org/github.com/pmylund/go-cache](http://godoc.org/github.com/pmylund/go-cache) +`godoc` or [http://godoc.org/github.com/patrickmn/go-cache](http://godoc.org/github.com/patrickmn/go-cache) From 2491a50c66c880504a7eb9be37305b53d3c5bf57 Mon Sep 17 00:00:00 2001 From: Giuseppe Date: Tue, 1 Dec 2015 18:38:44 +0100 Subject: [PATCH 17/17] modified test --- cache_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cache_test.go b/cache_test.go index fd9ccb5..ab6a547 100644 --- a/cache_test.go +++ b/cache_test.go @@ -1618,15 +1618,15 @@ func BenchmarkDeleteExpired(b *testing.B) { func BenchmarkLargeCache(b *testing.B) { b.StopTimer() - tc := New(200 * time.Millisecond, 50 * time.Millisecond) + tc := New(100 * time.Millisecond, 1 * time.Millisecond) //tc.mu.Lock() - for i := 0; i < 100000; i++ { + for i := 0; i < 1000000; i++ { tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) } //tc.mu.Unlock() tc.DeleteExpired() b.StartTimer() - for i := 100000; i <100000 + b.N; i++ { + for i := 1000000; i <1000000 + b.N; i++ { tc.Set(strconv.Itoa(i), "bar", DefaultExpiration) } }