@asamuzakjp/generational-cache 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +35 -31
  2. package/package.json +5 -3
package/README.md CHANGED
@@ -6,8 +6,6 @@
6
6
 
7
7
  A lightweight, **generational pseudo-LRU (Least Recently Used) cache** with strict maximum size limits.
8
8
 
9
- `GenerationalCache` uses a two-generation strategy (Current and Old) to provide a balance between memory efficiency and high-speed access, making it particularly effective for workloads with frequent evictions.
10
-
11
9
  ## How it Works
12
10
 
13
11
  `GenerationalCache` maintains two internal `Map` objects: `current` and `old`.
@@ -19,8 +17,9 @@ A lightweight, **generational pseudo-LRU (Least Recently Used) cache** with stri
19
17
  This "pseudo-LRU" approach avoids the overhead of updating timestamps or complex linked list pointers on every single access.
20
18
 
21
19
  ## Installation
22
-
23
- (Under development. Not yet published to npm.)
20
+ ```bash
21
+ npm i @asamuzakjp/generational-cache
22
+ ```
24
23
 
25
24
  ## Usage
26
25
  ```javascript
@@ -76,45 +75,50 @@ Benchmarks are divided into two states to simulate real-world conditions:
76
75
  ### Benchmark Environment
77
76
  - **Engine:** Node.js v24.x (V8)
78
77
  - **Measurement:** [mitata](https://github.com/evanwashere/mitata).
79
- - **Comparison:** [LRUCache](https://www.npmjs.com/package/lru-cache) (v11.x), [QuickLRU](https://www.npmjs.com/package/quick-lru) (v7.x)
78
+ - **Comparison:** [LRUCache](https://www.npmjs.com/package/lru-cache) (v11.x), [QuickLRU](https://www.npmjs.com/package/quick-lru) (v7.x), [Mnemonist](https://www.npmjs.com/package/mnemonist) (v0.40.x)
80
79
 
81
80
  ### 1. Small Cache (Max Size = 512)
82
- | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU |
83
- | :--- | :--- | :--- | :--- | :--- |
84
- | **Set** | Cold | **17,699,115 ops/sec** | 4,343,671 ops/sec | 15,057,973 ops/sec |
85
- | | Warm | **21,949,078 ops/sec** | 14,664,906 ops/sec | 17,067,759 ops/sec |
86
- | **Get** | Cold | **16,136,840 ops/sec** | 8,646,779 ops/sec | 13,262,599 ops/sec |
87
- | | Warm | 20,462,451 ops/sec | **22,351,363 ops/sec** | 15,398,829 ops/sec |
88
- | **Eviction** | Cold | **17,391,304 ops/sec** | 7,231,703 ops/sec | 14,788,524 ops/sec |
89
- | | Warm | **21,640,337 ops/sec** | 7,785,130 ops/sec | 15,656,802 ops/sec |
81
+ | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU | Mnemonist |
82
+ | :--- | :--- | :--- | :--- | :--- | :--- |
83
+ | **Set** | Cold | **17,733,640 ops/sec** | 4,933,885 ops/sec | 13,506,212 ops/sec | **17,229,496 ops/sec** |
84
+ | | Warm | **23,030,861 ops/sec** | 15,216,068 ops/sec | 18,175,209 ops/sec | 19,409,937 ops/sec |
85
+ | **Get** | Cold | 17,717,930 ops/sec | 7,633,587 ops/sec | 13,734,377 ops/sec | **30,731,407 ops/sec** |
86
+ | | Warm | 21,724,961 ops/sec | 24,148,756 ops/sec | 16,385,384 ops/sec | **35,688,793 ops/sec** |
87
+ | **Eviction** | Cold | **16,700,066 ops/sec** | 6,953,619 ops/sec | 13,285,505 ops/sec | 4,925,865 ops/sec |
88
+ | | Warm | **23,148,148 ops/sec** | 9,040,773 ops/sec | 16,903,313 ops/sec | 8,037,293 ops/sec |
90
89
 
91
90
  ### 2. Medium Cache (Max Size = 2,048)
92
- | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU |
93
- | :--- | :--- | :--- | :--- | :--- |
94
- | **Set** | Cold | **16,382,699 ops/sec** | 3,798,526 ops/sec | 12,558,081 ops/sec |
95
- | | Warm | **19,245,573 ops/sec** | 13,097,576 ops/sec | 14,940,983 ops/sec |
96
- | **Get** | Cold | **14,124,293 ops/sec** | 7,747,133 ops/sec | 11,675,423 ops/sec |
97
- | | Warm | 17,870,443 ops/sec | **18,238,190 ops/sec** | 13,477,088 ops/sec |
98
- | **Eviction** | Cold | **16,619,577 ops/sec** | 7,020,499 ops/sec | 11,646,866 ops/sec |
99
- | | Warm | **20,635,575 ops/sec** | 7,540,909 ops/sec | 13,908,205 ops/sec |
91
+ | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU | Mnemonist |
92
+ | :--- | :--- | :--- | :--- | :--- | :--- |
93
+ | **Set** | Cold | **15,987,210 ops/sec** | 4,874,957 ops/sec | 11,849,745 ops/sec | **15,309,246 ops/sec** |
94
+ | | Warm | **19,716,088 ops/sec** | 13,345,789 ops/sec | 14,755,791 ops/sec | 17,325,017 ops/sec |
95
+ | **Get** | Cold | 14,994,751 ops/sec | 7,950,389 ops/sec | 11,503,508 ops/sec | **23,651,844 ops/sec** |
96
+ | | Warm | 17,825,311 ops/sec | 18,789,928 ops/sec | 13,838,915 ops/sec | **31,289,111 ops/sec** |
97
+ | **Eviction** | Cold | **16,355,904 ops/sec** | 6,757,669 ops/sec | 12,074,378 ops/sec | 5,175,983 ops/sec |
98
+ | | Warm | **21,982,853 ops/sec** | 8,089,305 ops/sec | 15,309,246 ops/sec | 7,132,158 ops/sec |
100
99
 
101
100
  ### 3. Large Cache (Max Size = 8,192)
102
- | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU |
101
+ | Scenario | State | **GenerationalCache** | LRUCache | QuickLRU | Mnemonist |
102
+ | :--- | :--- | :--- | :--- | :--- | :--- |
103
+ | **Set** | Cold | **13,679,890 ops/sec** | 3,954,288 ops/sec | 8,126,777 ops/sec | 10,972,130 ops/sec |
104
+ | | Warm | **20,593,080 ops/sec** | 12,054,001 ops/sec | 12,995,451 ops/sec | 15,600,624 ops/sec |
105
+ | **Get** | Cold | 11,918,951 ops/sec | 5,785,363 ops/sec | 9,067,827 ops/sec | **16,784,155 ops/sec** |
106
+ | | Warm | 16,781,339 ops/sec | 17,247,326 ops/sec | 12,733,987 ops/sec | **31,436,655 ops/sec** |
107
+ | **Eviction** | Cold | **13,561,160 ops/sec** | 5,510,249 ops/sec | 9,642,271 ops/sec | 4,040,404 ops/sec |
108
+ | | Warm | **21,128,248 ops/sec** | 7,082,152 ops/sec | 13,208,294 ops/sec | 6,023,007 ops/sec |
109
+
110
+ ### 4. Cyclic Access (Max Size = 8,192 / Working Set = 5,000)
111
+ | Metric | **GenerationalCache** | LRUCache | QuickLRU | Mnemonist |
103
112
  | :--- | :--- | :--- | :--- | :--- |
104
- | **Set** | Cold | **15,216,068 ops/sec** | 3,727,587 ops/sec | 9,911,785 ops/sec |
105
- | | Warm | **19,432,568 ops/sec** | 11,793,843 ops/sec | 13,817,880 ops/sec |
106
- | **Get** | Cold | **11,542,012 ops/sec** | 5,955,216 ops/sec | 8,841,732 ops/sec |
107
- | | Warm | 17,322,016 ops/sec | **17,818,959 ops/sec** | 13,342,228 ops/sec |
108
- | **Eviction** | Cold | **13,340,448 ops/sec** | 5,236,973 ops/sec | 9,320,533 ops/sec |
109
- | | Warm | **19,409,937 ops/sec** | 6,889,424 ops/sec | 12,671,059 ops/sec |
113
+ | **Hit Rate** | 78.30% | **100.00%** | **100.00%** | **100.00%** |
114
+ | **Throughput** | 10,365,916 ops/sec | 40,832,993 ops/sec | 40,950,040 ops/sec | **48,426,150 ops/sec** |
110
115
 
111
- ### Key Characteristics
116
+ ## Key Characteristics
112
117
 
113
118
  * **High Eviction Efficiency**: `GenerationalCache` demonstrates strong throughput during high-turnover workloads, maintaining a performance margin compared to standard LRU designs in large-scale eviction scenarios.
114
119
  * **Predictable Scalability**: While other libraries may experience performance degradation as cache size increases, `GenerationalCache` maintains consistent throughput due to its generational swap mechanism.
115
120
  * **Balanced Read/Write**: It provides stable and competitive performance across all basic operations (`get`, `set`), making it suitable for both read-heavy and write-heavy environments.
116
- * **Conclusion**: As a developer, I'm more surprised than anyone by these benchmark results.
117
- I'm confident in the quality of the library, but benchmark results should probably be taken with a grain of salt :)
121
+ * **Trade-offs**: In cyclic access patterns where the working set is greater than `max / 2` but smaller than `max`, `GenerationalCache` will experience frequent generation swaps and cache misses. To maximize the performance benefits of `GenerationalCache`, it is often better to keep the `max` size small enough to allow some evictions, rather than trying to fit the entire working set.
118
122
 
119
123
  ## License
120
124
 
package/package.json CHANGED
@@ -31,9 +31,10 @@
31
31
  "eslint-plugin-prettier": "^5.5.5",
32
32
  "eslint-plugin-regexp": "^3.1.0",
33
33
  "eslint-plugin-unicorn": "^64.0.0",
34
- "globals": "^17.4.0",
35
- "lru-cache": "^11.3.3",
34
+ "globals": "^17.5.0",
35
+ "lru-cache": "^11.3.5",
36
36
  "mitata": "^1.0.34",
37
+ "mnemonist": "^0.40.3",
37
38
  "mocha": "^11.7.5",
38
39
  "neostandard": "^0.13.0",
39
40
  "prettier": "^3.8.2",
@@ -51,6 +52,7 @@
51
52
  },
52
53
  "scripts": {
53
54
  "bench": "node --expose-gc ./benchmark/benchmark.js",
55
+ "bench:worst": "node --expose-gc ./benchmark/worst-case-benchmark.js",
54
56
  "build": "npm run tsc && npm run lint && npm test",
55
57
  "lint": "eslint --fix .",
56
58
  "test": "c8 --reporter=text mocha --exit test/*.test.js",
@@ -59,5 +61,5 @@
59
61
  "engines": {
60
62
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
61
63
  },
62
- "version": "0.1.0"
64
+ "version": "1.0.1"
63
65
  }