This repo runs a benchmark suite, for a single-node environment, comparing:
no-cache— a no-cache baseline (always miss, direct loader call)lru-cache— using thelru-cachemodulebentocache(memory + Redis)tobolac(memory + SQLite)
The benchmark is focused only on measuring the performance of get/set (or getOrSet equivalent). The same TTL is used for all tested libraries, along with the same SWR value for those libraries that support it.
zipf- Requests follow a Zipf (power-law) distribution, where a small number of keys are very hot and most keys are cold. This tests steady-state cache behavior under skewed popularity.shiftingHotset- Most requests target a small hot window of keys, but that hot window shifts over time. This tests how quickly each cache adapts when the working set moves.
Running in a single-node environment, tobolac supports 2.5-5x times more operations than a two-layer Redis solution like bento, and 2.4-8.4x more than simple lru. This is not because tobolac is better than Redis or bento, or anything else, but simply because it has no network overhead, which is what you want for single-node use-cases.
All benchmarks ran on a machine with 8GB RAM and Intel Core i5 (quad). A relatively small, cheap server in 2026.
Ran with:
- 30s warmup, 150s measurement time
- 60s ttl, 30s swr
- 2048 concurrent clients
- loader latency random in the range of [60, 240]ms
- zipf exponent: 1.1
Operations per second:
| library/key space | 1k | 10k | 100k |
|---|---|---|---|
| no-cache | 13,603 | 13,590 | 13,600 |
| lru | 37,592 | 57,168 | 85,099 |
| bento-multitier | 61,132 | 79,597 | 79,597 |
| tobolac | 313,979 | 297,461 | 206,618 |
CPU Usage:
| library/key space | 1k | 10k | 100k |
|---|---|---|---|
| no-cache | 2.12 | 2.14 | 2.18 |
| lru | 3.4 | 4.3 | 6 |
| bento-multitier | 18.9 | 17.3 | 17.6 |
| tobolac | 12.94 | 13.04 | 13.02 |
- 30s warmup, 150s measurement time
- 60s ttl, 30s swr
- 2048 concurrent clients
- loader latency random in the range of [60, 240]ms
- hotset key count: 0.1 of total key space
- hotset key hit probability: 0.9
- hotsetPhaseOps: 50000
In effect: the hot set is 1% of keys, shifts every 50k ops, and 90% of accesses hit the current hot window.
Operations per second:
| library/key space | 1k | 10k | 100k |
|---|---|---|---|
| no-cache | 13,571 | 13,609 | 13,608 |
| lru | 97,498 | 93,183 | 51,928 |
| bento-multitier | 105,107 | 108,851 | 74,585 |
| tobolac | 459,531 | 376,200 | 246,387 |
CPU Usage:
| library/key space | 1k | 10k | 100k |
|---|---|---|---|
| no-cache | 2.04 | 2.06 | 2.10 |
| lru | 4.86 | 5.04 | 4.98 |
| bento-multitier | 18.96 | 19.18 | 19.04 |
| tobolac | 12.68 | 12.74 | 12.92 |
- Install dependencies:
npm install- Start Redis (Homebrew):
brew install redis
brew services start redis
redis-cli pingYou should see: PONG.
Default Redis URL is already set to: redis://127.0.0.1:6379/15.
Later, to stop redis, you can run brew services stop redis.
If you need a different one:
REDIS_URL=redis://127.0.0.1:6379/15 npm run benchDefault is all scenarios. You can run one scenario:
npm run bench -- --scenario zipf
npm run bench -- --scenario shiftingHotset
npm run bench -- --scenario allThe config.js file can be used to fine-tune the parameters of both scenarios.
- Console prints
ops/s, CPU, and memory for each case. - Files are written to
results/latest.jsonandresults/latest.csv.