Skip to content

Commit b2d42e0

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
optimize BenchmarkSetParallel performance
1 parent 5aa709d commit b2d42e0

File tree

1 file changed

+67
-13
lines changed

1 file changed

+67
-13
lines changed

s3fifo.go

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ type shard[K comparable, V any] struct {
9898
capacity int
9999
smallCap int
100100
ghostCap int
101+
102+
// Free lists for reducing allocations
103+
freeEntries *entry[K, V]
104+
freeGhostEntries *ghostEntry[K]
101105
}
102106

103107
// entryList is an intrusive doubly-linked list for cache entries.
@@ -289,6 +293,51 @@ func newShard[K comparable, V any](capacity int) *shard[K, V] {
289293
return s
290294
}
291295

296+
func (s *shard[K, V]) getEntry() *entry[K, V] {
297+
if s.freeEntries != nil {
298+
e := s.freeEntries
299+
s.freeEntries = e.next
300+
e.next = nil
301+
e.prev = nil
302+
return e
303+
}
304+
return &entry[K, V]{}
305+
}
306+
307+
func (s *shard[K, V]) putEntry(e *entry[K, V]) {
308+
var zeroK K
309+
var zeroV V
310+
e.key = zeroK
311+
e.value = zeroV
312+
e.expiryNano = 0
313+
e.accessed.Store(false)
314+
e.inSmall = false
315+
e.prev = nil
316+
317+
e.next = s.freeEntries
318+
s.freeEntries = e
319+
}
320+
321+
func (s *shard[K, V]) getGhostEntry() *ghostEntry[K] {
322+
if s.freeGhostEntries != nil {
323+
e := s.freeGhostEntries
324+
s.freeGhostEntries = e.next
325+
e.next = nil
326+
e.prev = nil
327+
return e
328+
}
329+
return &ghostEntry[K]{}
330+
}
331+
332+
func (s *shard[K, V]) putGhostEntry(e *ghostEntry[K]) {
333+
var zeroK K
334+
e.key = zeroK
335+
e.prev = nil
336+
337+
e.next = s.freeGhostEntries
338+
s.freeGhostEntries = e
339+
}
340+
292341
// getShard returns the shard for a given key using type-optimized hashing.
293342
// Uses bitwise AND with shardMask for fast modulo (numShards must be power of 2).
294343
// Fast paths for int, int64, and string keys avoid the type switch overhead entirely.
@@ -418,14 +467,14 @@ func (s *shard[K, V]) getOrSet(key K, value V, expiryNano int64) (V, bool) {
418467
inGhost = true
419468
s.ghost.remove(ghostEnt)
420469
delete(s.ghostKeys, key)
470+
s.putGhostEntry(ghostEnt)
421471
}
422472

423-
ent := &entry[K, V]{
424-
key: key,
425-
value: value,
426-
expiryNano: expiryNano,
427-
inSmall: !inGhost,
428-
}
473+
ent := s.getEntry()
474+
ent.key = key
475+
ent.value = value
476+
ent.expiryNano = expiryNano
477+
ent.inSmall = !inGhost
429478

430479
for s.small.len+s.main.len >= s.capacity {
431480
s.evict()
@@ -467,15 +516,15 @@ func (s *shard[K, V]) set(key K, value V, expiryNano int64) {
467516
inGhost = true
468517
s.ghost.remove(ghostEnt)
469518
delete(s.ghostKeys, key)
519+
s.putGhostEntry(ghostEnt)
470520
}
471521

472522
// Create new entry
473-
ent := &entry[K, V]{
474-
key: key,
475-
value: value,
476-
expiryNano: expiryNano,
477-
inSmall: !inGhost,
478-
}
523+
ent := s.getEntry()
524+
ent.key = key
525+
ent.value = value
526+
ent.expiryNano = expiryNano
527+
ent.inSmall = !inGhost
479528

480529
// Make room if at capacity
481530
for s.small.len+s.main.len >= s.capacity {
@@ -515,6 +564,7 @@ func (s *shard[K, V]) delete(key K) {
515564
}
516565

517566
delete(s.items, key)
567+
s.putEntry(ent)
518568
}
519569

520570
// evict removes one item according to S3-FIFO algorithm.
@@ -537,6 +587,7 @@ func (s *shard[K, V]) evictFromSmall() {
537587
// Not accessed - evict and track in ghost
538588
delete(s.items, ent.key)
539589
s.addToGhost(ent.key)
590+
s.putEntry(ent)
540591
return
541592
}
542593

@@ -556,6 +607,7 @@ func (s *shard[K, V]) evictFromMain() {
556607
if !ent.accessed.Swap(false) {
557608
// Not accessed - evict
558609
delete(s.items, ent.key)
610+
s.putEntry(ent)
559611
return
560612
}
561613

@@ -570,9 +622,11 @@ func (s *shard[K, V]) addToGhost(key K) {
570622
oldest := s.ghost.front()
571623
delete(s.ghostKeys, oldest.key)
572624
s.ghost.remove(oldest)
625+
s.putGhostEntry(oldest)
573626
}
574627

575-
ent := &ghostEntry[K]{key: key}
628+
ent := s.getGhostEntry()
629+
ent.key = key
576630
s.ghost.pushBack(ent)
577631
s.ghostKeys[key] = ent
578632
}

0 commit comments

Comments
 (0)