Skip to content

Instantly share code, notes, and snippets.

@albe
Last active August 23, 2020 12:20
Show Gist options
  • Save albe/39c7b79f46daa49d2cf373ffab3c4513 to your computer and use it in GitHub Desktop.
Save albe/39c7b79f46daa49d2cf373ffab3c4513 to your computer and use it in GitHub Desktop.
Benchmark nodejs Array vs UInt32Array
const Benchmark = require('benchmark');
const benchmarks = require('beautify-benchmark');
const Suite = new Benchmark.Suite('fixed array');
Suite.on('cycle', (event) => benchmarks.add(event.target));
Suite.on('complete', () => benchmarks.log());
const fileBuffer = Buffer.alloc(16 * 128);
let offs = 0;
for (let i = 0; i<128; i++, offs+=16) {
fileBuffer.writeUInt32LE(1 + i*4, offs);
fileBuffer.writeUInt32LE(2 + i*4, offs+4);
fileBuffer.writeUInt32LE(3 + i*4, offs+8);
fileBuffer.writeUInt32LE(4 + i*4, offs+12);
}
Suite.add('entry read', () => {
for (let i = 0; i<128; i++) {
let entry = new Array(4);
entry[0] = fileBuffer.readUInt32LE(i*16);
entry[1] = fileBuffer.readUInt32LE(i*16 + 4);
entry[1] = fileBuffer.readUInt32LE(i*16 + 8);
entry[1] = fileBuffer.readUInt32LE(i*16 + 12);
if (entry[0] !== i * 4 + 1) {
console.log('Invalid data at entry #' + i, entry[0]);
}
}
});
Suite.add('entryview read', () => {
for (let i = 0; i < 128; i++) {
let entry = new Uint32Array(fileBuffer.buffer, fileBuffer.byteOffset + i * 16, 4);
if (entry[0] !== i * 4 + 1) {
console.log('Invalid data at entry #' + i, entry[0]);
}
}
});
Suite.run();
> node --version
v12.18.3
> node bench-array
2 tests completed.
entry read x 1,180,097 ops/sec ±1.49% (92 runs sampled)
entryview read x 198,301 ops/sec ±1.00% (90 runs sampled)
> WHY?!
@albe
Copy link
Author

albe commented Aug 23, 2020

nodejs/help#2926

Maybe to clarify a bit: While JS engines probably could optimize out the typed array creation, at least V8 currently doesn’t. That comes with allocation and object creation overhead.

I once overhead a JS engine developer say (slightly tongue-in-cheek) that the only thing typed arrays are really better at than plain Arrays is fast passing of data between JS and native code :)

@albe
Copy link
Author

albe commented Aug 23, 2020

Another interesting observation: Creating a custom class, that just holds a reference to the buffer and offset, then uses readUInt32LE() on demand will perform at ~2,8M ops/sec for the above benchmark. So whatever UInt32Array does, it's more than just the object creation and referencing of the buffer for access (maybe some ref counting which involves an additional allocation?).
Note though that it will start to perform worse relative to amount of value accesses and slower than the array solution starting with accessing the four values more than once.

So that means: buffer read access < object property access and typed array instanciation < generic object instanciation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment