@@ -49,22 +49,29 @@ func hashString(s string) uint64 {
4949}
5050
5151const (
52- // maxFreq caps the frequency counter for eviction. Paper uses 3; 2 tuned via binary search.
53- maxFreq = 2
52+ // maxFreq caps the frequency counter for eviction. Paper uses 3; 5 tuned via binary search.
53+ // WARNING: Must be >= 2. Setting to 1 creates infinite loop in eviction (items with
54+ // freq=1 get promoted instead of evicted, causing evictFromSmall to never return true).
55+ maxFreq = 5
5456
5557 // maxPeakFreq caps peakFreq for death row admission decisions.
5658 maxPeakFreq = 21
5759
58- // deathRowPct is the percentage of globalMaxPeak required for death row admission.
59- deathRowPct = 1
60-
6160 // smallQueueRatio is the small queue size as per-mille of shard capacity.
62- // 90 % tuned via binary search for highest avg hitrate while meeting all goals .
63- smallQueueRatio = 900 // per-mille (divide by 1000)
61+ // 40 % tuned via binary search for highest avg hitrate (60.68% vs 59.40% at 90%) .
62+ smallQueueRatio = 138 // per-mille - TESTING
6463
6564 // ghostFPRate is the bloom filter false positive rate for ghost tracking.
6665 ghostFPRate = 0.00001
6766
67+ // ghostCapPerMille is ghost queue capacity as per-mille of cache size.
68+ // 0.75x tuned via binary search (61.30% vs 60.68% at 8x).
69+ ghostCapPerMille = 750 // per-mille
70+
71+ // deathRowThresholdPerMille scales the death row admission threshold.
72+ // 1000 = average peakFreq. Tuned: 1000-2000 optimal (61.356%).
73+ deathRowThresholdPerMille = 1000
74+
6875 // minDeathRowSize is the minimum death row slots.
6976 // Death row size scales with capacity to match pre-sharding behavior.
7077 minDeathRowSize = 8
@@ -225,7 +232,7 @@ func newS3FIFO[K comparable, V any](cfg *config) *s3fifo[K, V] {
225232 entries : xsync.NewMap [K , * entry [K , V ]](xsync .WithPresize (size )),
226233 capacity : size ,
227234 smallThresh : size * smallQueueRatio / 1000 ,
228- ghostCap : size * 8 , // 8x ghost capacity tuned via binary search
235+ ghostCap : size * ghostCapPerMille / 1000 ,
229236 ghostActive : newBloomFilter (size , ghostFPRate ),
230237 ghostAging : newBloomFilter (size , ghostFPRate ),
231238 deathRow : make ([]* entry [K , V ], deathRowSize ),
@@ -614,8 +621,11 @@ func (c *s3fifo[K, V]) sampleAvgPeakFreq() uint32 {
614621// If death row is full, the oldest pending entry is truly evicted.
615622func (c * s3fifo [K , V ]) sendToDeathRow (e * entry [K , V ]) {
616623 // Compute adaptive threshold by sampling current entries.
617- // Only admit entries with above-average frequency to death row.
618- threshold := c .sampleAvgPeakFreq ()
624+ // Only admit entries with above-threshold frequency to death row.
625+ threshold := c .sampleAvgPeakFreq () * deathRowThresholdPerMille / 1000
626+ if threshold == 0 {
627+ threshold = 1
628+ }
619629 if e .peakFreq .Load () < threshold {
620630 c .entries .Delete (e .key )
621631 c .addToGhost (e .hash , e .peakFreq .Load ())
0 commit comments