Populating a pre-allocated array slower than a pushing to a regular array?
Henry Williams
Posted on July 19, 2020
For no good reason, I got the urge to do a performance comparison between populating an array by pushing to it vs writing to a buffer. Then, to make things more interesting, I decided to add a static array and a pre-allocated standard array.
Let's just say the results were not what I expected.
Experiment
Populate the 4 data structures by adding 10^8 elements to each and comparing the time it took for each of them.
Candidates
- Static array - populated by writing directly to the index
- Pre-allocated dynamic array - array initialized to hold all elements and then populated by setting elements for each index
- Buffer - populated by writing directly the offset. Should be similar to writing to an index, but there might be some internal overhead
- Array - empty array populated by pushing elements to it
Expected results (from fastest to slowest)
- Static array
- Pre-allocated array
- Buffer
- Array
Actual results (from fastest to slowest)
- Static array (228.039ms)
- Buffer (1.135s)
- Array (2.545s)
- Pre-allocated array (6.062s) (Why so slow???)
What I don't understand is why the pre-allocated array performed so poorly. I would expect its performance to be on par with a static array. I definitely didn't expect it to be outperformed by an array built by pushing elements into it.
Code
const NUMBER_OF_ELEMENTS = 10**8
const ELEMENT_LEN_BYTES = 4
const array = []
console.time('array')
for (let i = 1; i <= NUMBER_OF_ELEMENTS; i++) {
array.push(i)
}
console.timeEnd('array')
const preAllocatedArray = new Array(NUMBER_OF_ELEMENTS)
console.time('pre-allocated array')
for (let i = 1; i <= NUMBER_OF_ELEMENTS; i++) {
preAllocatedArray[i - 1] = i
}
console.timeEnd('pre-allocated array')
const intArray = new Uint32Array(NUMBER_OF_ELEMENTS)
console.time('int array')
for (let i = 0; i < NUMBER_OF_ELEMENTS; i++) {
intArray[i] = i + 1
}
console.timeEnd('int array')
const buffer = Buffer.alloc(NUMBER_OF_ELEMENTS * ELEMENT_LEN_BYTES, 0)
console.time('buffer')
for (let i = 1, offset = 0; i <= NUMBER_OF_ELEMENTS; i++) {
offset = buffer.writeUInt32BE(i, offset)
}
console.timeEnd('buffer')
// Results:
// array: 2.545s
// pre-allocated array: 6.062s
// int array: 228.039ms
// buffer: 1.135s
Edit: It looks like the V8 engine's optimizations favor .push()
over direct index assignment. The findings for Chrome in [this (ancient) article] are consistent with my results on Edge, Chrome, and Nodejs; all of which run on top of the v8 engine.
Thanks @alain Van Hout for sharing the link in the comments.
If anyone has any ideas how those optimizations are performed, please do share 🙂
Posted on July 19, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.