Skip to content

Commit

Permalink
Expand available size of cache, and enforce cache size limit
Browse files Browse the repository at this point in the history
  • Loading branch information
kriszyp committed Dec 16, 2021
1 parent 5b442f5 commit 208e94d
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 19 deletions.
38 changes: 20 additions & 18 deletions LRFUExpirer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ export const EXPIRED_ENTRY = {
description: 'This cache entry value has been expired from the LRFU cache, and is waiting for garbage collection to be removed.'
}
/* bit pattern:
* < is-in-lru 1 bit > ...< mask/or bits 4 bits > <lru index 4 bits > < position in cache - 16 bits >
* < is-in-lru 1 bit > ...< mask/or bits 6 bits > <lru index 2 bits > < position in cache - 22 bits >
*/
export class LRFUExpirer {
constructor(options) {
this.lruSize = options && options.lruSize || 0x2000
if (this.lruSize > 0x400000)
throw new Error('The LRU/cache size was larger than the maximum cache size of 16777216 (LRU size of 4194304)')
this.reset()
startTimedCleanup(new WeakRef(this), options && options.cleanupInterval || 60000)
}
delete(entry) {
if (entry.position < NOT_IN_LRU) {
this.lru[(entry.position >> 16) & 15][entry.position & 0xffff] = null
this.lru[(entry.position >> 22) & 3][entry.position & 0x3fffff] = null
}
entry.position |= NOT_IN_LRU
}
Expand All @@ -24,7 +26,7 @@ export class LRFUExpirer {
if (expirationPriority < 0) {
// pin this in memory, first remove from LRFU and then mark it as pinned in memory
if (entry.position < NOT_IN_LRU) {
this.lru[(entry.position >> 16) & 15][entry.position & 0xffff] = null
this.lru[(entry.position >> 22) & 3][entry.position & 0x3fffff] = null
}
entry.position = PINNED_IN_MEMORY
return
Expand All @@ -41,49 +43,49 @@ export class LRFUExpirer {
expirationPriority = bits
} else {
if (originalPosition >= 0)
expirationPriority = (originalPosition >> 20) & 15
expirationPriority = (originalPosition >> 24) & 0x3f
else
expirationPriority = 0
}

let lruPosition
let lruIndex
if (originalPosition < NOT_IN_LRU) {
lruIndex = (originalPosition >> 16) & 15
lruIndex = (originalPosition >> 22) & 3
if (lruIndex >= 3)
return // can't get any higher than this, don't do anything
let lru = this.lru[lruIndex]
// check to see if it is in the same generation
lruPosition = lru.position
if ((originalPosition > lruPosition ? lruPosition + this.lruSize : lruPosition) - originalPosition < (this.lruSize >> 3))
return // only recently added, don't promote
lru[originalPosition & 0xffff] = null // remove it, we are going to move/promote it
lru[originalPosition & 0x3fffff] = null // remove it, we are going to move/promote it
lruIndex++
} else
lruIndex = 0
this.insertEntry(entry, lruIndex, expirationPriority)
}
insertEntry(entry, lruIndex, expirationPriority) {
let lruPosition, nextLru = this.lru[lruIndex]
let orMask = 0xffff >> (16 - expirationPriority)
let orMask = 0x3fffff >> (22 - expirationPriority)
do {
// put it in the next lru
lruPosition = nextLru.position | orMask
let previousEntry = nextLru[lruPosition & 0xffff]
nextLru[lruPosition & 0xffff] = entry
let previousEntry = nextLru[lruPosition & 0x3fffff]
nextLru[lruPosition & 0x3fffff] = entry
if (entry)
entry.position = lruPosition | (expirationPriority << 20)
entry.position = lruPosition | (expirationPriority << 24)
nextLru.position = ++lruPosition
if ((lruPosition & 0xffff) >= this.lruSize) {
if ((lruPosition & 0x3fffff) >= this.lruSize) {
// reset at the beginning of the lru cache
lruPosition &= 0x7fff0000
lruPosition &= 0x7fc00000
nextLru.position = lruPosition
nextLru.cycles++
}
entry = previousEntry
if (entry && (nextLru = this.lru[--lruIndex])) {
expirationPriority = ((entry.position || 0) >> 20) & 15
orMask = 0xffff >> (16 - expirationPriority)
expirationPriority = ((entry.position || 0) >> 24) & 0x3f
orMask = 0x3fffff >> (22 - expirationPriority)
} else
break
} while (true)
Expand Down Expand Up @@ -113,7 +115,7 @@ export class LRFUExpirer {
this.lru = []
for (let i = 0; i < 4; i++) {
this.lru[i] = new Array(this.lruSize)
this.lru[i].position = i << 16
this.lru[i].position = i << 22
this.lru[i].cycles = 0
}
}
Expand All @@ -122,13 +124,13 @@ export class LRFUExpirer {
for (let i = 3; i >= 0; i--) {
let lru = this.lru[i]
for (let j = 0, l = toClear; j < l; j++) {
if (lru[lru.position & 0xffff]) {
if (lru[lru.position & 0x3fffff]) {
toClear--
this.insertEntry(null, i, 0)
} else {
if ((++lru.position & 0xffff) >= this.lruSize) {
if ((++lru.position & 0x3fffff) >= this.lruSize) {
// reset at the beginning of the lru cache
lru.position &= 0x7fff0000
lru.position &= 0x7fc00000
lru.cycles++
}
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The `expirationPriority` can also be set to `-1` that the value should be `pinne
The `WeakLRUCache` constructor supports an optional `options` object parameter that allows specific configuration and cache tuning. The following properties (all optional) can be defined on the `options` object:

### cacheSize
This indicates the number of entries to allocate in the cache. This defaults to 32,768.
This indicates the number of entries to allocate in the cache. This defaults to 32,768, and the maximum allowed value is 16,777,216.

### expirer
By default there is a single shared expiration cache, that is a single instance of `LRFUExpirer`. However, you can define your own expiration cache or create separate instances of LRFUExpirer. Generally using a (the default) single instance is preferable since it naturally gives higher priority/recency to more heavily used caches, and allows lesser used caches to expire more of their entries.
Expand Down

0 comments on commit 208e94d

Please sign in to comment.