Let’s take a look at an example:
The two functions –
buildArray2 do the same thing. They iterate
n times and add the current index value into an array in the index position.
I beg to differ
The difference between the two functions is in the array initiation line:
Measuring the runtime of these functions with 20000000 array members shows us that
buildArray2 is much faster (figure 1).
You’d might say that 20000000 is not a valid number of array members. You are correct in most cases. You should remember, though, that this is a very simple example in which the array members are integers.
In addition, you’d might have a process that runs periodically. The difference between the above functions, multiplied by the time your process repeats can become substantial as you scale (a rendering cycle for instance).
I’ve prepared a live demo with which you can play with the size of the array as well as the number of times you repeat the iteration: https://yonatankra.com/performance/memory/demos/gcAndAllocation
If we use the demo above to test our code and set our arrays to 1000 members, with 20000 iterations we see the results in figure 2. These results show us that even with relatively small arrays, we’d might get a performance hit in certain cases.
For instance, imagine a function that recreates our small array for immutability. This function might run in response a user’s click. It can also run in response to socket messages that can come rapidly.
The function done without pre-allocation will take less time to run – and the difference will stack.
Cause and Effect
In order to understand the cause for these differences, we can profile using the chrome performance tool.
The results of the profiling of the 20000000 array members run is shown in Figure 3.
Figure 3a shows a long runtime with several garbage collection (GC) cycles. Multiple garbage collections are an indication for massive and inefficient memory allocation. Garbage collection happens when the JS engine has some data it does not need anymore and needs to dispose of it.
Figure 4 shows that pushing to the array took much more time per iteration than just changing a value in a pre allocated array. That’s because there was no need to allocate memory during the run.
Because we are using an array, the JS engine tries to optimize by maintaining the array as a C++ array. For this, the array must be contiguous in memory.
Because of that, the JS engine keeps allocating and reallocating memory for the whole array every time – and needs to dispose of the old chunks of memory in the “old place”. This is the garbage collection we see.
Figure 3b shows that if we preallocated the memory beforehand, the JS engine already did the allocation when we started and hence does not need to do the whole reallocation and garbage collection as we go.
What we also learn here is this: if our array size is relatively small and we do not repeat this process a lot, then preallocation might be less performant since the pre-allocation at the beginning will be very costly.
In this article, we learned what is memory allocation and garbage collection.
Memory allocation is the process in which the software (in this case, the JS engine) finds room in memory for variables. Garbage collection is the process in which the JS engine removes obsolete data from memory.
Both descriptions here were brief, as the main point was to actually be able to see the effects of the allocation and GC.
Of course, these two terms that usually go hand in hand have a lot more under the hood and you can read more about them.
I recommend this really nice article about garbage collection.
You can play around with the demo and see the results for yourself.
Quick question: set the Array Size to 1000 and the iterations to 1 and look at the result in the console. What do you see? Can you explain the result after what we’ve learned?