forked from hypergeometric/notepack
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf(decode): add a cache for buffer-to-string conversions
The tiny and small benchmarks were greatly improved: +--------------+-------------------+-----------------+----------------+-------------+ | │ tiny │ small │ medium │ large | +--------------+-------------------+-----------------+----------------+-------------+ | before │ 1,712,916 ops/sec │ 369,462 ops/sec │ 30,692 ops/sec │ 252 ops/sec | +--------------+-------------------+-----------------+----------------+-------------+ | after │ 3,138,712 ops/sec │ 642,057 ops/sec │ 34,143 ops/sec │ 252 ops/sec | +--------------+-------------------+-----------------+----------------+-------------+ Note: only object keys are cached.
- Loading branch information
1 parent
9bf1519
commit 3c0e5a6
Showing
3 changed files
with
112 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
const DEFAULT_MAX_SIZE = process.env.NOTEPACK_DECODE_KEY_CACHE_MAX_SIZE || 1024; | ||
const DEFAULT_MAX_LENGTH = process.env.NOTEPACK_DECODE_KEY_CACHE_MAX_LENGTH || 16; | ||
|
||
/** | ||
* Store the buffer-to-string values in a tree | ||
*/ | ||
class DecodeKeyCache { | ||
constructor({ maxSize = DEFAULT_MAX_SIZE, maxLength = DEFAULT_MAX_LENGTH } = {}) { | ||
this.size = 0; | ||
this.maxSize = maxSize; | ||
this.maxLength = maxLength; | ||
this.cache = new Map(); | ||
for (let i = 1; i <= this.maxLength; i++) { | ||
this.cache.set(i, new Map()); | ||
} | ||
} | ||
|
||
get(buffer, offset, length) { | ||
if (length > this.maxLength) { return false; } | ||
let node = this.cache.get(length); | ||
for (let i = 0; i < length; i++) { | ||
const byte = buffer[offset + i]; | ||
if (node.has(byte)) { | ||
node = node.get(byte); | ||
} else { | ||
return false; | ||
} | ||
} | ||
return node; | ||
} | ||
|
||
set(buffer, offset, length, value) { | ||
if (length > this.maxLength || this.size >= this.maxSize) { return; } | ||
this.size++; | ||
let node = this.cache.get(length); | ||
for (let i = 0; i < length; i++) { | ||
const byte = buffer[offset + i]; | ||
if (i === length - 1) { | ||
node.set(byte, value); | ||
} else if (node.has(byte)) { | ||
node = node.get(byte); | ||
} else { | ||
const newNode = new Map(); | ||
node.set(byte, newNode); | ||
node = newNode; | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = DecodeKeyCache; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
|
||
const expect = require('chai').expect; | ||
const DecodeKeyCache = require('../lib/DecodeKeyCache'); | ||
|
||
describe('DecodeKeyCache', () => { | ||
it('should get/set values', () => { | ||
const cache = new DecodeKeyCache(); | ||
cache.set(Buffer.from([1, 1]), 0, 2, '11'); | ||
cache.set(Buffer.from([1, 2]), 0, 2, '12'); | ||
cache.set(Buffer.from([1, 2, 3]), 0, 3, '123'); | ||
cache.set(Buffer.from([1, 2, 3, 4]), 1, 2, '23'); | ||
|
||
expect(cache.get(Buffer.from([1, 1]), 0, 2)).to.equal('11'); | ||
expect(cache.get(Buffer.from([1, 2, 3, 4]), 0, 2)).to.equal('12'); | ||
expect(cache.get(Buffer.from([0, 1, 2, 3, 4]), 2, 2)).to.equal('23'); | ||
expect(cache.get(Buffer.from([1, 3]), 0, 1)).to.equal(false); | ||
expect(cache.get(null, null, 17)).to.equal(false); | ||
}); | ||
|
||
it('should respect maxLength limit', () => { | ||
const cache = new DecodeKeyCache({ maxLength: 3 }); | ||
cache.set(Buffer.from([1]), 0, 1, '1'); | ||
cache.set(Buffer.from([1, 2]), 0, 2, '12'); | ||
cache.set(Buffer.from([1, 2, 3]), 0, 3, '123'); | ||
cache.set(Buffer.from([1, 2, 3, 4]), 0, 4, '1234'); | ||
|
||
expect(cache.size).to.equal(3); | ||
expect(cache.get(Buffer.from([1, 2, 3, 4]), 0, 4)).to.equal(false); | ||
}); | ||
|
||
it('should respect maxSize limit', () => { | ||
const cache = new DecodeKeyCache({ maxSize: 2 }); | ||
cache.set(Buffer.from([1]), 0, 1, '1'); | ||
cache.set(Buffer.from([1, 2]), 0, 2, '12'); | ||
cache.set(Buffer.from([1, 2, 3]), 0, 3, '123'); | ||
cache.set(Buffer.from([1, 2, 3, 4]), 0, 4, '1234'); | ||
|
||
expect(cache.size).to.equal(2); | ||
expect(cache.get(Buffer.from([1, 2, 3]), 0, 3)).to.equal(false); | ||
}); | ||
|
||
}); | ||
|