Pre-allocate arrays to make Javascript fast
Tags: javascript, performance, webdev, ux
Published: 2024-05-18 14:05:11

If you ever implemented a dynamic array in low level languages like C++ you allocate a chunk of memory for several elements at once, when there's no more space for new elements you allocate a new chunk. I guess this is valid and for Javascript arrays. Allocation of a memory on the heap is an expensive operation. 

Let's try in Chrome's console (several times, the output is the most frequent):
```js
const start = performance.memory.usedJSHeapSize;
const arr = new Array(1);
setTimeout(() => {
    const diff = performance.memory.usedJSHeapSize - start;
    console.log('heap allocated', diff);
}, 100);

> heap allocated 1688
```
```js
const start = performance.memory.usedJSHeapSize;
const arr = new Array(1_000_000);
setTimeout(() => {
    const diff = performance.memory.usedJSHeapSize - start;
    console.log('heap allocated', diff);
}, 100);

> heap allocated 4001684
```
As we see with 1_000_000 array length we have 3.8Mb allocated.

Of course the reason behind the optimization could be different or composite, I'm not a developer of any current JS engines, but it seems that changing the length of an array is generally an expensive operation.

Regardless of the actual JS implementation if we follow the rule:

> When the final size of an array is known during creation of the array - allocate it:
```js
const arr = Array(knownSize);
```
This immediately boosts your Javascript speed:
```js
let $length = 10;
const arr = Array.from({length: $length}, (_, i) => i);

// @benchmark non pre-allocated
{
  const result = [];
  for(let i = 0; i < arr.length; i++){
    result.push(arr[i]);
  }
  result;
}

// @benchmark pre-allocated
{
  const result = Array(arr.length);
  for(let i = 0; i < arr.length; i++){
    result[i] = arr[i];
  }
  result;
}

// @benchmark Array#map
arr.map(i => i);

` Chrome/125
-----------------------------------------------------------------------------------------------
>                         n=10       |      n=100       |      n=1000       |      n=10000     
pre-allocated       ■ 1.00x x10m  85 | ■ 1.00x x10m 669 | ■ 1.00x   x1m 474 | ■ 1.00x x100k 560
Array#map             1.69x x10m 144 |   1.79x  x1m 120 |   2.15x x100k 102 |   3.20x  x10k 179
non pre-allocated     3.22x x10m 274 |   3.57x  x1m 239 |   4.11x x100k 195 |   3.66x  x10k 205
----------------------------------------------------------------------------------------------- `
` Firefox/126
---------------------------------------------------------------------------------------------
>                         n=10       |      n=100      |      n=1000       |     n=10000     
pre-allocated       ■ 1.00x x10m 227 | ■ 1.00x x1m 162 | ■ 1.00x x100k 192 | ■ 1.00x x10k 208
non pre-allocated     1.44x x10m 327 |   1.45x x1m 235 |   1.16x x100k 222 |   1.53x x10k 319
Array#map             1.46x x10m 332 |   1.06x x1m 172 |   1.73x x100k 333 |   2.17x x10k 452
--------------------------------------------------------------------------------------------- `
```
[Open in the playground](https://silentmantra.github.io/benchmark#H4sIAAAAAAAACp2QwU7DMBBEf2UUerDVNi3XGiP4jjhCJnWJhbOONu4BRfl35LiIA5eKyx52Z2fe7lx18eyqUxVcwiY4%2Bkg9NB6PylAXaUqwzNB4ZbZf9YXjIOaiOv3Ilx3E2w5eQj%2FDS2XI0OGAl3dHXT9Y%2FgRFwshub0OInU3ubGg2BJQAdtM1JGg0rcrdS2SRaTw0jgoeT5mhLmEKfruV6zZum%2FV4nXphmRvfytVhyaUMlaHlL9AdMOvB4jdY%2Fout8S00Ctt9aGvuw2BHQ9l5sKPwt8dWyzfk7DtsrgEAAA%3D%3D)

Keep in mind though that you create a [sparse array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays) with `Array(size)` so some methods like `map()`, `forEach()` wouldn't work as expected, but the iterators works fine like `[...arr]`.