Open PandaWorker opened 1 month ago
Code
interface ReusifyItem { next: this | null; } function reusify<T extends ReusifyItem>(Constructor: new () => T) { let head: T | null = null; let tail: T | null = null; head = tail = new Constructor(); function acquire(): T { var current = head; if (current?.next) { head = current.next; } else { tail = head = new Constructor(); } current!.next = null; return current!; } function release(obj: T) { tail!.next = obj; tail = obj; } return { acquire: acquire, release: release, }; } class Reusify<T extends ReusifyItem> { private head: T | null; private tail: T | null; private Constructor: new () => T; constructor(Constructor: new () => T) { this.tail = this.head = new Constructor(); this.Constructor = Constructor; } public acquire(): T { var current = this.head; if (current?.next) { this.head = current.next; current.next = null; } else { this.tail = this.head = new this.Constructor(); } return current!; } public release(obj: T): void { this.tail = this.tail!.next = obj; } }
Tests and benchmarks
// tests import assert from 'node:assert/strict'; class Task { next = null; } function MyTask() { // @ts-expect-error: this.next = null; } Deno.test({ name: 'func (task instance class)', fn: () => { const instance = reusify(Task); var obj = instance.acquire(); var obj2 = instance.acquire(); var obj3 = instance.acquire(); assert(obj !== obj2); assert(obj2 !== obj3); assert(obj3 !== obj); assert.notEqual(obj, obj2); assert.notEqual(obj2, obj3); assert.notEqual(obj3, obj); assert.equal(obj.next, null); assert.equal(obj2.next, null); assert.equal(obj3.next, null); instance.release(obj); instance.release(obj2); instance.release(obj3); // skip one instance.acquire(); var obj4 = instance.acquire(); var obj5 = instance.acquire(); var obj6 = instance.acquire(); assert(obj4 === obj, '4'); assert(obj5 === obj2, '5'); assert(obj6 === obj3, '6'); }, }); Deno.test({ name: 'func (task instance object or func)', fn: () => { const instance = reusify(Task); var obj = instance.acquire(); var obj2 = instance.acquire(); var obj3 = instance.acquire(); assert(obj !== obj2); assert(obj2 !== obj3); assert(obj3 !== obj); assert.notEqual(obj, obj2); assert.notEqual(obj2, obj3); assert.notEqual(obj3, obj); assert.equal(obj.next, null); assert.equal(obj2.next, null); assert.equal(obj3.next, null); instance.release(obj); instance.release(obj2); instance.release(obj3); // skip one instance.acquire(); var obj4 = instance.acquire(); var obj5 = instance.acquire(); var obj6 = instance.acquire(); assert(obj4 === obj, '4'); assert(obj5 === obj2, '5'); assert(obj6 === obj3, '6'); }, }); Deno.test({ name: 'class (task instance object or func)', fn: () => { // @ts-expect-error: const instance = new Reusify(MyTask); var obj = instance.acquire(); var obj2 = instance.acquire(); var obj3 = instance.acquire(); assert(obj !== obj2); assert(obj2 !== obj3); assert(obj3 !== obj); assert.notEqual(obj, obj2); assert.notEqual(obj2, obj3); assert.notEqual(obj3, obj); assert.equal(obj.next, null); assert.equal(obj2.next, null); assert.equal(obj3.next, null); instance.release(obj); instance.release(obj2); instance.release(obj3); // skip one instance.acquire(); var obj4 = instance.acquire(); var obj5 = instance.acquire(); var obj6 = instance.acquire(); assert(obj4 === obj, '4'); assert(obj5 === obj2, '5'); assert(obj6 === obj3, '6'); }, }); Deno.test({ name: 'class (task instance class)', fn: () => { const instance = new Reusify(Task); var obj = instance.acquire(); var obj2 = instance.acquire(); var obj3 = instance.acquire(); assert(obj !== obj2); assert(obj2 !== obj3); assert(obj3 !== obj); assert.notEqual(obj, obj2); assert.notEqual(obj2, obj3); assert.notEqual(obj3, obj); assert.equal(obj.next, null); assert.equal(obj2.next, null); assert.equal(obj3.next, null); instance.release(obj); instance.release(obj2); instance.release(obj3); // skip one instance.acquire(); var obj4 = instance.acquire(); var obj5 = instance.acquire(); var obj6 = instance.acquire(); assert(obj4 === obj, '4'); assert(obj5 === obj2, '5'); assert(obj6 === obj3, '6'); }, }); // benchmarks Deno.bench({ name: 'func (taks instance func)', group: 'reusify', fn: () => { // @ts-expect-error: const queue = reusify(MyTask); for (let i = 0; i < 10_000; i++) { const task1 = queue.acquire(); const task2 = queue.acquire(); const task3 = queue.acquire(); queue.release(task1); queue.release(task2); queue.release(task3); } }, }); Deno.bench({ name: 'class (task instance func)', group: 'reusify', fn: () => { // @ts-expect-error: const queue = new Reusify(MyTask); for (let i = 0; i < 10_000; i++) { const task1 = queue.acquire(); const task2 = queue.acquire(); const task3 = queue.acquire(); queue.release(task1); queue.release(task2); queue.release(task3); } }, }); Deno.bench({ name: 'func (task instance class)', group: 'reusify', fn: () => { const queue = reusify(Task); for (let i = 0; i < 10_000; i++) { const task1 = queue.acquire(); const task2 = queue.acquire(); const task3 = queue.acquire(); queue.release(task1); queue.release(task2); queue.release(task3); } }, }); Deno.bench({ name: 'class (task instance class)', group: 'reusify', fn: () => { const queue = new Reusify(Task); for (let i = 0; i < 10_000; i++) { const task1 = queue.acquire(); const task2 = queue.acquire(); const task3 = queue.acquire(); queue.release(task1); queue.release(task2); queue.release(task3); } }, });
Benchmarks results
CPU | Apple M1 Max Runtime | Deno 1.46.3 (aarch64-apple-darwin) benchmark time/iter (avg) iter/s (min … max) p75 p99 p995 ----------------------------- ----------------------------- --------------------- -------------------------- group reusify func (taks instance func) 130.3 µs 7,673 (128.2 µs … 253.1 µs) 131.3 µs 147.0 µs 153.8 µs class (task instance func) 82.8 µs 12,070 ( 78.0 µs … 286.2 µs) 82.9 µs 107.2 µs 117.5 µs func (task instance class) 181.7 µs 5,503 (172.0 µs … 372.2 µs) 183.1 µs 213.1 µs 228.7 µs class (task instance class) 112.6 µs 8,881 (108.5 µs … 304.2 µs) 112.5 µs 152.2 µs 160.5 µs summary class (task instance func) 1.36x faster than class (task instance class) 1.57x faster than func (taks instance func) 2.19x faster than func (task instance class)
Wow, seems nicer! Would you like to send a PR?
Code
Tests and benchmarks
Benchmarks results