@@ -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