Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
falsandtru committed Feb 28, 2024
1 parent 9f98b75 commit f70c31c
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 75 deletions.
85 changes: 41 additions & 44 deletions benchmark/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('Benchmark:', function () {
benchmark('DWC new', () => new Cache(10000), done);
});

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
it(`Clock set miss ${size.toLocaleString('en')}`, function (done) {
const cache = new Clock<number, object>(size);
for (let i = 0; i < Math.ceil(size / 32) * 32; ++i) cache.set(~i, {});
Expand Down Expand Up @@ -84,7 +84,7 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
it(`Clock set hit ${size.toLocaleString('en')}`, function (done) {
const cache = new Clock<number, object>(size);
for (let i = 0; i < Math.ceil(size / 32) * 32; ++i) cache.set(i, {});
Expand Down Expand Up @@ -128,7 +128,7 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
it(`Clock get miss ${size.toLocaleString('en')}`, function (done) {
const cache = new Clock<number, object>(size);
for (let i = 0; i < Math.ceil(size / 32) * 32; ++i) cache.set(~i, {});
Expand Down Expand Up @@ -172,7 +172,7 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
it(`Clock get hit ${size.toLocaleString('en')}`, function (done) {
const cache = new Clock<number, object>(size);
for (let i = 0; i < Math.ceil(size / 32) * 32; ++i) cache.set(i, {});
Expand Down Expand Up @@ -216,21 +216,15 @@ describe('Benchmark:', function () {
});
}

// 1e7はシミュだけ実行するとISCが単体でもGitHub Actionsの次の環境とエラーで落ちる。
// ベンチ全体を実行したときはなぜか落ちない。
//
// Error: Uncaught RangeError: Map maximum size exceeded (dist/index.js:16418)
//
// System:
// OS: Linux 5.15 Ubuntu 20.04.5 LTS (Focal Fossa)
// CPU: (2) x64 Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz
// Memory: 5.88 GB / 6.78 GB
//
for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
const bias = (capacity: number, rng: () => number) => () => rng() * capacity * 10 | 0;
// 遅いZipfを速い疑似関数で代用し偏りを再現する。
// ILRUのリストをインデクスで置き換える高速化手法はZipfのような
// 最も典型的な偏りのアクセスパターンで50%以下の速度に低速化する場合がある。
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
const pzipf = (capacity: number, rng: () => number) => () =>
Math.floor((rng() * capacity) ** 2 / (5 * capacity / 100 | 0));
it(`Clock simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new Clock<number, object>(size);
const random = bias(Math.ceil(size / 32) * 32, xorshift.random(1));
const random = pzipf(Math.ceil(size / 32) * 32, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`Clock simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -240,7 +234,7 @@ describe('Benchmark:', function () {

it(`ILRU simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new LRUCache<number, object>({ max: size });
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`ILRU simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -250,7 +244,7 @@ describe('Benchmark:', function () {

it(`LRU simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new LRU<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`LRU simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -260,7 +254,7 @@ describe('Benchmark:', function () {

it(`TRC-C simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new TRCC<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-C simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -270,7 +264,7 @@ describe('Benchmark:', function () {

it(`TRC-L simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new TRCL<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-L simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -280,7 +274,7 @@ describe('Benchmark:', function () {

it(`DWC simulation ${size.toLocaleString('en')} 10%`, function (done) {
const cache = new Cache<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`DWC simulation ${size.toLocaleString('en')} 10%`, () => {
const key = random();
Expand All @@ -289,11 +283,12 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
const bias = (capacity: number, rng: () => number) => () => rng() * capacity * 2 | 0;
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
const pzipf = (capacity: number, rng: () => number) => () =>
Math.floor((rng() * capacity) ** 2 / (35 * capacity / 100 | 0));
it(`Clock simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new Clock<number, object>(size);
const random = bias(Math.ceil(size / 32) * 32, xorshift.random(1));
const random = pzipf(Math.ceil(size / 32) * 32, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`Clock simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -303,7 +298,7 @@ describe('Benchmark:', function () {

it(`ILRU simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new LRUCache<number, object>({ max: size });
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`ILRU simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -313,7 +308,7 @@ describe('Benchmark:', function () {

it(`LRU simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new LRU<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`LRU simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -323,7 +318,7 @@ describe('Benchmark:', function () {

it(`TRC-C simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new TRCC<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-C simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -333,7 +328,7 @@ describe('Benchmark:', function () {

it(`TRC-L simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new TRCL<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-L simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -343,7 +338,7 @@ describe('Benchmark:', function () {

it(`DWC simulation ${size.toLocaleString('en')} 50%`, function (done) {
const cache = new Cache<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`DWC simulation ${size.toLocaleString('en')} 50%`, () => {
const key = random();
Expand All @@ -352,11 +347,12 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
const bias = (capacity: number, rng: () => number) => () => rng() * capacity * 1.1 | 0;
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
const pzipf = (capacity: number, rng: () => number) => () =>
Math.floor((rng() * capacity) ** 2 / (85 * capacity / 100 | 0));
it(`Clock simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new Clock<number, object>(size);
const random = bias(Math.ceil(size / 32) * 32, xorshift.random(1));
const random = pzipf(Math.ceil(size / 32) * 32, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`Clock simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -366,7 +362,7 @@ describe('Benchmark:', function () {

it(`ILRU simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new LRUCache<number, object>({ max: size });
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`ILRU simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -376,7 +372,7 @@ describe('Benchmark:', function () {

it(`LRU simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new LRU<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`LRU simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -386,7 +382,7 @@ describe('Benchmark:', function () {

it(`TRC-C simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new TRCC<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-C simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -396,7 +392,7 @@ describe('Benchmark:', function () {

it(`TRC-L simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new TRCL<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`TRC-L simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -406,7 +402,7 @@ describe('Benchmark:', function () {

it(`DWC simulation ${size.toLocaleString('en')} 90%`, function (done) {
const cache = new Cache<number, object>(size);
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 10; ++i) cache.set(random(), {});
benchmark(`DWC simulation ${size.toLocaleString('en')} 90%`, () => {
const key = random();
Expand All @@ -415,14 +411,15 @@ describe('Benchmark:', function () {
});
}

for (const size of [1e1, 1e2, 1e3, 1e4, 1e5, 1e6]) {
const bias = (capacity: number, rng: () => number) => () => rng() * capacity * 1.1 | 0;
for (const size of [1e2, 1e3, 1e4, 1e5, 1e6]) {
const pzipf = (capacity: number, rng: () => number) => () =>
Math.floor((rng() * capacity) ** 2 / (85 * capacity / 100 | 0));
const age = 1000;
it(`ILRU simulation ${size.toLocaleString('en')} 90% expire`, captureTimers(function (done) {
const cache = new LRUCache<number, object>({ max: size, ttlAutopurge: true });
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 9; ++i) cache.set(random(), {});
for (let i = 0; i < size * 1; ++i) cache.set(i, {}, { ttl: age });
for (let i = 0; i < size * 1; ++i) cache.set(random(), {}, { ttl: age });
benchmark(`ILRU simulation ${size.toLocaleString('en')} 90% expire`, () => {
const key = random();
cache.get(key) ?? cache.set(key, {}, { ttl: age });
Expand All @@ -431,9 +428,9 @@ describe('Benchmark:', function () {

it(`DWC simulation ${size.toLocaleString('en')} 90% expire`, function (done) {
const cache = new Cache<number, object>(size, { eagerExpiration: true });
const random = bias(size, xorshift.random(1));
const random = pzipf(size, xorshift.random(1));
for (let i = 0; i < size * 9; ++i) cache.set(random(), {});
for (let i = 0; i < size * 1; ++i) cache.set(i, {}, { age: age });
for (let i = 0; i < size * 1; ++i) cache.set(random(), {}, { age: age });
benchmark(`DWC simulation ${size.toLocaleString('en')} 90% expire`, () => {
const key = random();
cache.get(key) ?? cache.add(key, {}, { age: age });
Expand Down
46 changes: 40 additions & 6 deletions src/cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,39 @@ describe('Unit: lib/cache', () => {
assert(stats.dwc / stats.lru * 100 >>> 0 === 147);
});

it('ratio pzipf 100', function () {
this.timeout(10 * 1e3);

const capacity = 100;
const lru = new LRU<number, 1>(capacity);
const trc = new TLRU<number, 1>(capacity);
const dwc = new Cache<number, 1>(capacity);

const trials = capacity * 1000;
const random = xorshift.random(1);
const stats = new Stats();
const log = { 0: 0 };
for (let i = 0; i < trials; ++i) {
const key = Math.floor((random() * capacity) ** 2 / (10 * capacity / 100 | 0));
stats.lru += lru.get(key) ?? +lru.set(key, 1) & 0;
stats.trc += trc.get(key) ?? +trc.set(key, 1) & 0;
stats.dwc += dwc.get(key) ?? +dwc.set(key, 1) & 0;
stats.total += 1;
key in log ? ++log[key] : log[key] = 1;
}
//console.debug(Object.entries(log).sort((a, b) => b[1] - a[1]).map(e => [+e[0], e[1]]));
assert(dwc['LRU'].length + dwc['LFU'].length === dwc['dict'].size);
assert(dwc['dict'].size <= capacity);
console.debug('Cache pzipf 100');
console.debug('LRU hit ratio', stats.lru * 100 / stats.total);
console.debug('TRC hit ratio', stats.trc * 100 / stats.total);
console.debug('DWC hit ratio', stats.dwc * 100 / stats.total);
console.debug('DWC / LRU hit ratio', `${stats.dwc / stats.lru * 100 | 0}%`);
console.debug('DWC ratio', dwc['partition']! * 100 / capacity | 0, dwc['LFU'].length * 100 / capacity | 0);
console.debug('DWC overlap', dwc['overlapLRU'], dwc['overlapLFU']);
assert(stats.dwc / stats.lru * 100 >>> 0 === 126);
});

it('ratio zipf 100', function () {
this.timeout(10 * 1e3);

Expand Down Expand Up @@ -803,7 +836,7 @@ describe('Unit: lib/cache', () => {
assert(dwc['partition']! * 100 / capacity >>> 0 === 0);
});

it('ratio uneven 1,000', function () {
it('ratio pzipf 1,000', function () {
this.timeout(60 * 1e3);

const capacity = 1000;
Expand All @@ -814,25 +847,26 @@ describe('Unit: lib/cache', () => {
const trials = capacity * 1000;
const random = xorshift.random(1);
const stats = new Stats();
const log = { 0: 0 };
for (let i = 0; i < trials; ++i) {
const key = random() < 0.4
? random() * capacity * -1 | 0
: random() * capacity * 10 | 0;
const key = Math.floor((random() * capacity) ** 2 / (10 * capacity / 100 | 0));
stats.lru += lru.get(key) ?? +lru.set(key, 1) & 0;
stats.trc += trc.get(key) ?? +trc.set(key, 1) & 0;
stats.dwc += dwc.get(key) ?? +dwc.set(key, 1) & 0;
stats.total += 1;
key in log ? ++log[key] : log[key] = 1;
}
//console.debug(Object.entries(log).sort((a, b) => b[1] - a[1]).map(e => [+e[0], e[1]]));
assert(dwc['LRU'].length + dwc['LFU'].length === dwc['dict'].size);
assert(dwc['dict'].size <= capacity);
console.debug('Cache uneven 1,000');
console.debug('Cache pzipf 1,000');
console.debug('LRU hit ratio', stats.lru * 100 / stats.total);
console.debug('TRC hit ratio', stats.trc * 100 / stats.total);
console.debug('DWC hit ratio', stats.dwc * 100 / stats.total);
console.debug('DWC / LRU hit ratio', `${stats.dwc / stats.lru * 100 | 0}%`);
console.debug('DWC ratio', dwc['partition']! * 100 / capacity | 0, dwc['LFU'].length * 100 / capacity | 0);
console.debug('DWC overlap', dwc['overlapLRU'], dwc['overlapLFU']);
assert(stats.dwc / stats.lru * 100 >>> 0 === 162);
assert(stats.dwc / stats.lru * 100 >>> 0 === 130);
});

});
Expand Down
Loading

0 comments on commit f70c31c

Please sign in to comment.