Skip to content

Commit 901b241

Browse files
committed
Improve cache locality by removing Item-related pointers
1 parent 3d4d09c commit 901b241

File tree

3 files changed

+55
-24
lines changed

3 files changed

+55
-24
lines changed

cache.go

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ import (
1010
"time"
1111
)
1212

13+
var emptyTime = time.Time{}
14+
1315
type Item struct {
1416
Object interface{}
15-
Expiration *time.Time
17+
Expiration time.Time
1618
}
1719

1820
// Returns true if the item has expired.
19-
func (item *Item) Expired() bool {
20-
if item.Expiration == nil {
21+
func (item Item) Expired() bool {
22+
if item.Expiration == emptyTime {
2123
return false
2224
}
2325
return item.Expiration.Before(time.Now())
@@ -39,7 +41,7 @@ type Cache struct {
3941

4042
type cache struct {
4143
defaultExpiration time.Duration
42-
items map[string]*Item
44+
items map[string]Item
4345
mu sync.RWMutex
4446
onEvicted func(string, interface{})
4547
janitor *janitor
@@ -57,15 +59,14 @@ func (c *cache) Set(k string, x interface{}, d time.Duration) {
5759
}
5860

5961
func (c *cache) set(k string, x interface{}, d time.Duration) {
60-
var e *time.Time
62+
e := emptyTime
6163
if d == DefaultExpiration {
6264
d = c.defaultExpiration
6365
}
6466
if d > 0 {
65-
t := time.Now().Add(d)
66-
e = &t
67+
e = time.Now().Add(d)
6768
}
68-
c.items[k] = &Item{
69+
c.items[k] = Item{
6970
Object: x,
7071
Expiration: e,
7172
}
@@ -159,6 +160,7 @@ func (c *cache) Increment(k string, n int64) error {
159160
c.mu.Unlock()
160161
return fmt.Errorf("The value for %s is not an integer", k)
161162
}
163+
c.items[k] = v
162164
c.mu.Unlock()
163165
return nil
164166
}
@@ -184,6 +186,7 @@ func (c *cache) IncrementFloat(k string, n float64) error {
184186
c.mu.Unlock()
185187
return fmt.Errorf("The value for %s does not have type float32 or float64", k)
186188
}
189+
c.items[k] = v
187190
c.mu.Unlock()
188191
return nil
189192
}
@@ -205,6 +208,7 @@ func (c *cache) IncrementInt(k string, n int) (int, error) {
205208
}
206209
nv := rv + n
207210
v.Object = nv
211+
c.items[k] = v
208212
c.mu.Unlock()
209213
return nv, nil
210214
}
@@ -226,6 +230,7 @@ func (c *cache) IncrementInt8(k string, n int8) (int8, error) {
226230
}
227231
nv := rv + n
228232
v.Object = nv
233+
c.items[k] = v
229234
c.mu.Unlock()
230235
return nv, nil
231236
}
@@ -247,6 +252,7 @@ func (c *cache) IncrementInt16(k string, n int16) (int16, error) {
247252
}
248253
nv := rv + n
249254
v.Object = nv
255+
c.items[k] = v
250256
c.mu.Unlock()
251257
return nv, nil
252258
}
@@ -268,6 +274,7 @@ func (c *cache) IncrementInt32(k string, n int32) (int32, error) {
268274
}
269275
nv := rv + n
270276
v.Object = nv
277+
c.items[k] = v
271278
c.mu.Unlock()
272279
return nv, nil
273280
}
@@ -289,6 +296,7 @@ func (c *cache) IncrementInt64(k string, n int64) (int64, error) {
289296
}
290297
nv := rv + n
291298
v.Object = nv
299+
c.items[k] = v
292300
c.mu.Unlock()
293301
return nv, nil
294302
}
@@ -310,6 +318,7 @@ func (c *cache) IncrementUint(k string, n uint) (uint, error) {
310318
}
311319
nv := rv + n
312320
v.Object = nv
321+
c.items[k] = v
313322
c.mu.Unlock()
314323
return nv, nil
315324
}
@@ -331,6 +340,7 @@ func (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) {
331340
}
332341
nv := rv + n
333342
v.Object = nv
343+
c.items[k] = v
334344
c.mu.Unlock()
335345
return nv, nil
336346
}
@@ -352,6 +362,7 @@ func (c *cache) IncrementUint8(k string, n uint8) (uint8, error) {
352362
}
353363
nv := rv + n
354364
v.Object = nv
365+
c.items[k] = v
355366
c.mu.Unlock()
356367
return nv, nil
357368
}
@@ -373,6 +384,7 @@ func (c *cache) IncrementUint16(k string, n uint16) (uint16, error) {
373384
}
374385
nv := rv + n
375386
v.Object = nv
387+
c.items[k] = v
376388
c.mu.Unlock()
377389
return nv, nil
378390
}
@@ -394,6 +406,7 @@ func (c *cache) IncrementUint32(k string, n uint32) (uint32, error) {
394406
}
395407
nv := rv + n
396408
v.Object = nv
409+
c.items[k] = v
397410
c.mu.Unlock()
398411
return nv, nil
399412
}
@@ -415,6 +428,7 @@ func (c *cache) IncrementUint64(k string, n uint64) (uint64, error) {
415428
}
416429
nv := rv + n
417430
v.Object = nv
431+
c.items[k] = v
418432
c.mu.Unlock()
419433
return nv, nil
420434
}
@@ -436,6 +450,7 @@ func (c *cache) IncrementFloat32(k string, n float32) (float32, error) {
436450
}
437451
nv := rv + n
438452
v.Object = nv
453+
c.items[k] = v
439454
c.mu.Unlock()
440455
return nv, nil
441456
}
@@ -457,6 +472,7 @@ func (c *cache) IncrementFloat64(k string, n float64) (float64, error) {
457472
}
458473
nv := rv + n
459474
v.Object = nv
475+
c.items[k] = v
460476
c.mu.Unlock()
461477
return nv, nil
462478
}
@@ -506,6 +522,7 @@ func (c *cache) Decrement(k string, n int64) error {
506522
c.mu.Unlock()
507523
return fmt.Errorf("The value for %s is not an integer", k)
508524
}
525+
c.items[k] = v
509526
c.mu.Unlock()
510527
return nil
511528
}
@@ -531,6 +548,7 @@ func (c *cache) DecrementFloat(k string, n float64) error {
531548
c.mu.Unlock()
532549
return fmt.Errorf("The value for %s does not have type float32 or float64", k)
533550
}
551+
c.items[k] = v
534552
c.mu.Unlock()
535553
return nil
536554
}
@@ -552,6 +570,7 @@ func (c *cache) DecrementInt(k string, n int) (int, error) {
552570
}
553571
nv := rv - n
554572
v.Object = nv
573+
c.items[k] = v
555574
c.mu.Unlock()
556575
return nv, nil
557576
}
@@ -573,6 +592,7 @@ func (c *cache) DecrementInt8(k string, n int8) (int8, error) {
573592
}
574593
nv := rv - n
575594
v.Object = nv
595+
c.items[k] = v
576596
c.mu.Unlock()
577597
return nv, nil
578598
}
@@ -594,6 +614,7 @@ func (c *cache) DecrementInt16(k string, n int16) (int16, error) {
594614
}
595615
nv := rv - n
596616
v.Object = nv
617+
c.items[k] = v
597618
c.mu.Unlock()
598619
return nv, nil
599620
}
@@ -615,6 +636,7 @@ func (c *cache) DecrementInt32(k string, n int32) (int32, error) {
615636
}
616637
nv := rv - n
617638
v.Object = nv
639+
c.items[k] = v
618640
c.mu.Unlock()
619641
return nv, nil
620642
}
@@ -636,6 +658,7 @@ func (c *cache) DecrementInt64(k string, n int64) (int64, error) {
636658
}
637659
nv := rv - n
638660
v.Object = nv
661+
c.items[k] = v
639662
c.mu.Unlock()
640663
return nv, nil
641664
}
@@ -657,6 +680,7 @@ func (c *cache) DecrementUint(k string, n uint) (uint, error) {
657680
}
658681
nv := rv - n
659682
v.Object = nv
683+
c.items[k] = v
660684
c.mu.Unlock()
661685
return nv, nil
662686
}
@@ -678,6 +702,7 @@ func (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) {
678702
}
679703
nv := rv - n
680704
v.Object = nv
705+
c.items[k] = v
681706
c.mu.Unlock()
682707
return nv, nil
683708
}
@@ -699,6 +724,7 @@ func (c *cache) DecrementUint8(k string, n uint8) (uint8, error) {
699724
}
700725
nv := rv - n
701726
v.Object = nv
727+
c.items[k] = v
702728
c.mu.Unlock()
703729
return nv, nil
704730
}
@@ -720,6 +746,7 @@ func (c *cache) DecrementUint16(k string, n uint16) (uint16, error) {
720746
}
721747
nv := rv - n
722748
v.Object = nv
749+
c.items[k] = v
723750
c.mu.Unlock()
724751
return nv, nil
725752
}
@@ -741,6 +768,7 @@ func (c *cache) DecrementUint32(k string, n uint32) (uint32, error) {
741768
}
742769
nv := rv - n
743770
v.Object = nv
771+
c.items[k] = v
744772
c.mu.Unlock()
745773
return nv, nil
746774
}
@@ -762,6 +790,7 @@ func (c *cache) DecrementUint64(k string, n uint64) (uint64, error) {
762790
}
763791
nv := rv - n
764792
v.Object = nv
793+
c.items[k] = v
765794
c.mu.Unlock()
766795
return nv, nil
767796
}
@@ -783,6 +812,7 @@ func (c *cache) DecrementFloat32(k string, n float32) (float32, error) {
783812
}
784813
nv := rv - n
785814
v.Object = nv
815+
c.items[k] = v
786816
c.mu.Unlock()
787817
return nv, nil
788818
}
@@ -804,6 +834,7 @@ func (c *cache) DecrementFloat64(k string, n float64) (float64, error) {
804834
}
805835
nv := rv - n
806836
v.Object = nv
837+
c.items[k] = v
807838
c.mu.Unlock()
808839
return nv, nil
809840
}
@@ -906,7 +937,7 @@ func (c *cache) SaveFile(fname string) error {
906937
// documentation for NewFrom().)
907938
func (c *cache) Load(r io.Reader) error {
908939
dec := gob.NewDecoder(r)
909-
items := map[string]*Item{}
940+
items := map[string]Item{}
910941
err := dec.Decode(&items)
911942
if err == nil {
912943
c.mu.Lock()
@@ -944,7 +975,7 @@ func (c *cache) LoadFile(fname string) error {
944975
// fields of the items should be checked. Note that explicit synchronization
945976
// is needed to use a cache and its corresponding Items() return value at
946977
// the same time, as the map is shared.
947-
func (c *cache) Items() map[string]*Item {
978+
func (c *cache) Items() map[string]Item {
948979
c.mu.RLock()
949980
defer c.mu.RUnlock()
950981
return c.items
@@ -962,7 +993,7 @@ func (c *cache) ItemCount() int {
962993
// Delete all items from the cache.
963994
func (c *cache) Flush() {
964995
c.mu.Lock()
965-
c.items = map[string]*Item{}
996+
c.items = map[string]Item{}
966997
c.mu.Unlock()
967998
}
968999

@@ -997,7 +1028,7 @@ func runJanitor(c *cache, ci time.Duration) {
9971028
go j.Run(c)
9981029
}
9991030

1000-
func newCache(de time.Duration, m map[string]*Item) *cache {
1031+
func newCache(de time.Duration, m map[string]Item) *cache {
10011032
if de == 0 {
10021033
de = -1
10031034
}
@@ -1008,7 +1039,7 @@ func newCache(de time.Duration, m map[string]*Item) *cache {
10081039
return c
10091040
}
10101041

1011-
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item) *Cache {
1042+
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache {
10121043
c := newCache(de, m)
10131044
// This trick ensures that the janitor goroutine (which--granted it
10141045
// was enabled--is running DeleteExpired on c forever) does not keep
@@ -1029,7 +1060,7 @@ func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item)
10291060
// manually. If the cleanup interval is less than one, expired items are not
10301061
// deleted from the cache before calling c.DeleteExpired().
10311062
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
1032-
items := make(map[string]*Item)
1063+
items := make(map[string]Item)
10331064
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
10341065
}
10351066

@@ -1042,7 +1073,7 @@ func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
10421073
// NewFrom() also accepts an items map which will serve as the underlying map
10431074
// for the cache. This is useful for starting from a deserialized cache
10441075
// (serialized using e.g. gob.Encode() on c.Items()), or passing in e.g.
1045-
// make(map[string]*Item, 500) to improve startup performance when the cache
1076+
// make(map[string]Item, 500) to improve startup performance when the cache
10461077
// is expected to reach a certain minimum size.
10471078
//
10481079
// Only the cache's methods synchronize access to this map, so it is not
@@ -1054,6 +1085,6 @@ func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
10541085
// gob.Register() the individual types stored in the cache before encoding a
10551086
// map retrieved with c.Items(), and to register those same types before
10561087
// decoding a blob containing an items map.
1057-
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]*Item) *Cache {
1088+
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache {
10581089
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
10591090
}

cache_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ func TestCacheTimes(t *testing.T) {
107107
}
108108

109109
func TestNewFrom(t *testing.T) {
110-
m := map[string]*Item{
111-
"a": &Item{
110+
m := map[string]Item{
111+
"a": Item{
112112
Object: 1,
113-
Expiration: nil,
113+
Expiration: emptyTime,
114114
},
115-
"b": &Item{
115+
"b": Item{
116116
Object: 2,
117-
Expiration: nil,
117+
Expiration: emptyTime,
118118
},
119119
}
120120
tc := NewFrom(DefaultExpiration, 0, m)

sharded.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ func (sc *shardedCache) DeleteExpired() {
109109
// fields of the items should be checked. Note that explicit synchronization
110110
// is needed to use a cache and its corresponding Items() return values at
111111
// the same time, as the maps are shared.
112-
func (sc *shardedCache) Items() []map[string]*Item {
113-
res := make([]map[string]*Item, len(sc.cs))
112+
func (sc *shardedCache) Items() []map[string]Item {
113+
res := make([]map[string]Item, len(sc.cs))
114114
for i, v := range sc.cs {
115115
res[i] = v.Items()
116116
}
@@ -171,7 +171,7 @@ func newShardedCache(n int, de time.Duration) *shardedCache {
171171
for i := 0; i < n; i++ {
172172
c := &cache{
173173
defaultExpiration: de,
174-
items: map[string]*Item{},
174+
items: map[string]Item{},
175175
}
176176
sc.cs[i] = c
177177
}

0 commit comments

Comments
 (0)