spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
146 lines (143 loc) • 3.46 kB
text/typescript
// https://jasony.me/publication/sosp23-s3fifo.pdf
import { min } from './alias';
import { Queue } from './queue';
class Node<K, V> {
constructor(
public key: K,
public value: V,
) {
}
public resident = true;
public freq = 0;
}
export class S3FIFO<K, V> {
constructor(
private readonly capacity: number,
) {
assert(capacity > 0);
}
private readonly capS = this.capacity * 0.1 >>> 0;
private readonly capM = this.capacity - this.capS;
private readonly dict = new Map<K, Node<K, V>>();
private readonly fifoS = new Queue<Node<K, V>>();
private readonly fifoM = new Queue<Node<K, V>>();
private readonly fifoG = new Queue<Node<K, undefined>>();
public get length(): number {
return this.fifoS.length + this.fifoM.length;
}
public get size(): number {
return this.fifoS.length + this.fifoM.length;
}
private read(x: Node<K, V>, counting = true): void {
if (x.resident) {
if (!counting) return;
x.freq = min(x.freq + 1, 3);
}
else {
this.insert(x);
x.freq = 0;
}
}
private insert(x: Node<K, V>): void {
if (this.length === this.capacity) {
this.evict();
}
if (x.resident) {
this.fifoS.push(x);
}
else {
x.resident = true;
this.fifoM.push(x);
}
}
private evict(): void {
if (this.fifoS.length >= this.capS) {
this.evictS();
}
else {
this.evictM();
}
}
private evictS(): void {
while (!this.fifoS.isEmpty()) {
const t = this.fifoS.pop()!;
if (t.freq > 1) {
this.fifoM.push(t);
if (this.fifoM.length >= this.capM) {
this.evictM();
}
}
else {
t.resident = false;
// @ts-expect-error
t.value = undefined;
if (this.fifoG.length >= this.capM) {
this.evictG();
}
this.fifoG.push(t as Node<K, undefined>);
return;
}
}
}
private evictM(): void {
while (!this.fifoM.isEmpty()) {
const t = this.fifoM.pop()!;
if (t.freq > 0) {
this.fifoM.push(t);
t.freq = t.freq - 1;
}
else {
this.dict.delete(t.key);
return;
}
}
}
private evictG(): void {
while (!this.fifoG.isEmpty()) {
const t = this.fifoG.pop()!;
if (!t.resident) {
this.dict.delete(t.key);
}
return;
}
}
public set(key: K, value: V): this {
const node = this.dict.get(key);
if (node === undefined) {
const node = new Node(key, value);
this.insert(node);
this.dict.set(key, node);
}
else {
node.value = value;
this.read(node, false);
}
assert(this.dict.size <= this.capacity + this.capM);
assert(this.length <= this.capacity);
assert(this.fifoG.length <= this.capM);
return this;
}
public get(key: K): V | undefined {
const { dict } = this;
const node = dict.get(key);
if (node === undefined || !node.resident) return;
this.read(node);
return node.value;
}
public has(key: K): boolean {
return this.dict.get(key)?.resident === true;
}
public clear(): void {
this.dict.clear();
this.fifoS.clear();
this.fifoM.clear();
this.fifoG.clear();
}
public *[Symbol.iterator](): Iterator<[K, V], undefined, undefined> {
for (const fifo of [this.fifoS, this.fifoM]) {
for (const { key, value } of fifo) {
yield [key, value];
}
}
}
}