Skip to content

Commit 30502c2

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
micro-optimization for time
1 parent 02b796b commit 30502c2

File tree

1 file changed

+43
-34
lines changed

1 file changed

+43
-34
lines changed

s3fifo.go

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import (
88
"time"
99
)
1010

11-
const numShards = 32
11+
const (
12+
numShards = 32
13+
shardMask = numShards - 1 // For fast modulo via bitwise AND
14+
)
1215

1316
// s3fifo implements the S3-FIFO eviction algorithm from SOSP'23 paper
1417
// "FIFO queues are all you need for cache eviction"
1518
//
16-
// This implementation uses 16-way sharding for improved concurrent performance.
19+
// This implementation uses 32-way sharding for improved concurrent performance.
1720
// Each shard is an independent S3-FIFO instance with its own queues and lock.
1821
//
1922
// Algorithm per shard:
@@ -38,7 +41,11 @@ type s3fifo[K comparable, V any] struct {
3841
}
3942

4043
// shard is an independent S3-FIFO cache partition.
44+
//
45+
//nolint:govet // fieldalignment: padding is intentional to prevent false sharing
4146
type shard[K comparable, V any] struct {
47+
mu sync.RWMutex
48+
_ [40]byte // Padding to prevent false sharing (mutex is 24 bytes, pad to 64-byte cache line)
4249
items map[K]*entry[K, V]
4350
small *list.List
4451
main *list.List
@@ -47,17 +54,16 @@ type shard[K comparable, V any] struct {
4754
capacity int
4855
smallCap int
4956
ghostCap int
50-
mu sync.RWMutex
5157
}
5258

5359
// entry represents a cached item with metadata.
5460
type entry[K comparable, V any] struct {
55-
expiry time.Time
56-
key K
57-
value V
58-
element *list.Element
59-
accessed atomic.Bool // Fast "was accessed" flag for S3-FIFO promotion
60-
inSmall bool // True if in Small queue, false if in Main
61+
key K
62+
value V
63+
element *list.Element
64+
expiryNano int64 // Unix nanoseconds; 0 means no expiry
65+
accessed atomic.Bool // Fast "was accessed" flag for S3-FIFO promotion
66+
inSmall bool // True if in Small queue, false if in Main
6167
}
6268

6369
// newS3FIFO creates a new sharded S3-FIFO cache with the given total capacity.
@@ -109,35 +115,29 @@ func newShard[K comparable, V any](capacity int) *shard[K, V] {
109115
}
110116

111117
// getShard returns the shard for a given key using type-optimized hashing.
118+
// Uses bitwise AND with shardMask for fast modulo (numShards must be power of 2).
112119
func (c *s3fifo[K, V]) getShard(key K) *shard[K, V] {
113120
switch k := any(key).(type) {
114121
case int:
115122
if k < 0 {
116123
k = -k
117124
}
118-
return c.shards[k%numShards]
125+
return c.shards[k&shardMask]
119126
case int64:
120127
if k < 0 {
121128
k = -k
122129
}
123-
return c.shards[k%numShards]
130+
return c.shards[k&shardMask]
124131
case uint:
125-
return c.shards[k%numShards]
132+
return c.shards[k&shardMask]
126133
case uint64:
127-
return c.shards[k%numShards]
134+
return c.shards[k&shardMask]
128135
case string:
129-
var h maphash.Hash
130-
h.SetSeed(c.seed)
131-
//nolint:gosec // G104: maphash.Hash.WriteString never returns error
132-
h.WriteString(k)
133-
return c.shards[h.Sum64()%numShards]
136+
return c.shards[maphash.String(c.seed, k)&shardMask]
134137
default:
135138
// Fallback for other types: convert to string and hash
136-
var h maphash.Hash
137-
h.SetSeed(c.seed)
138-
//nolint:errcheck,gosec // maphash.Hash.WriteString never returns error
139-
h.WriteString(any(key).(string))
140-
return c.shards[h.Sum64()%numShards]
139+
//nolint:errcheck,forcetypeassert // Only called for string-convertible types
140+
return c.shards[maphash.String(c.seed, any(key).(string))&shardMask]
141141
}
142142
}
143143

@@ -157,7 +157,8 @@ func (s *shard[K, V]) get(key K) (V, bool) {
157157
}
158158

159159
// Fast path: check expiration while holding read lock
160-
if !ent.expiry.IsZero() && time.Now().After(ent.expiry) {
160+
// Single int64 comparison; time.Now().UnixNano() only called if item has expiry
161+
if ent.expiryNano != 0 && time.Now().UnixNano() > ent.expiryNano {
161162
s.mu.RUnlock()
162163
var zero V
163164
return zero, false
@@ -175,18 +176,26 @@ func (s *shard[K, V]) get(key K) (V, bool) {
175176
return val, true
176177
}
177178

179+
// timeToNano converts time.Time to Unix nanoseconds; zero time becomes 0.
180+
func timeToNano(t time.Time) int64 {
181+
if t.IsZero() {
182+
return 0
183+
}
184+
return t.UnixNano()
185+
}
186+
178187
// setToMemory adds or updates a value in the in-memory cache.
179188
func (c *s3fifo[K, V]) setToMemory(key K, value V, expiry time.Time) {
180-
c.getShard(key).set(key, value, expiry)
189+
c.getShard(key).set(key, value, timeToNano(expiry))
181190
}
182191

183-
func (s *shard[K, V]) set(key K, value V, expiry time.Time) {
192+
func (s *shard[K, V]) set(key K, value V, expiryNano int64) {
184193
s.mu.Lock()
185194

186195
// Update existing entry
187196
if ent, ok := s.items[key]; ok {
188197
ent.value = value
189-
ent.expiry = expiry
198+
ent.expiryNano = expiryNano
190199
s.mu.Unlock()
191200
return
192201
}
@@ -201,10 +210,10 @@ func (s *shard[K, V]) set(key K, value V, expiry time.Time) {
201210

202211
// Create new entry
203212
ent := &entry[K, V]{
204-
key: key,
205-
value: value,
206-
expiry: expiry,
207-
inSmall: !inGhost,
213+
key: key,
214+
value: value,
215+
expiryNano: expiryNano,
216+
inSmall: !inGhost,
208217
}
209218

210219
// Make room if at capacity
@@ -343,11 +352,11 @@ func (s *shard[K, V]) cleanup() int {
343352
s.mu.Lock()
344353
defer s.mu.Unlock()
345354

346-
now := time.Now()
355+
nowNano := time.Now().UnixNano()
347356
var expired []K
348357

349358
for key, ent := range s.items {
350-
if !ent.expiry.IsZero() && now.After(ent.expiry) {
359+
if ent.expiryNano != 0 && nowNano > ent.expiryNano {
351360
expired = append(expired, key)
352361
}
353362
}
@@ -420,6 +429,6 @@ func (c *s3fifo[K, V]) setExpiry(key K, expiry time.Time) {
420429
s.mu.Lock()
421430
defer s.mu.Unlock()
422431
if ent, ok := s.items[key]; ok {
423-
ent.expiry = expiry
432+
ent.expiryNano = timeToNano(expiry)
424433
}
425434
}

0 commit comments

Comments
 (0)