Performance Implications of Immutability

Learn how copying data and garbage collection enhances the performance of Elixir.

We'll cover the following

It’s easy to assume that copying data instead of updating the existing data is inefficient. After all, we have to create a new copy of data whenever we update it, and that’s going to leave lots of old values around to be garbage collected. Let’s look at these in detail below.

Copying data

Although common sense might dictate that all this copying of data is inefficient, the reverse is true. Because Elixir knows that existing data is immutable, it can reuse it when building new structures.

Consider the following code. It uses a new operator, [ head | tail ], which builds a new list with head as its first element and tail as the rest.

Note: We have a whole chapter on this when we talk about lists and recursion…

iex> list1 = [ 3, 2, 1 ] 
[3, 2, 1]
iex> list2 = [ 4 | list1 ] 
[4, 3, 2, 1]

Since Elixir knows list1 will never change, it simply constructs a new list with a head of 4 and a tail of list1.

Garbage collection

The other performance issue with transformational language is that we often leave old values unused when we create new values from them. This leaves a bunch of things using up memory on the heap, so the garbage collector has to reclaim them.

Most modern languages have a garbage collector, and developers have grown to be suspicious of them. They can impact performance quite badly.

But the cool thing about Elixir is that we write our code using lots and lots of processes, and each process has its own heap. The data in our application is divided up between these processes, so each individual heap is much smaller than it would’ve been if all the data had been in a single heap. As a result, garbage collection runs faster. If a process terminates before its heap becomes full, all its data is discarded. No garbage collection is required.

Get hands-on with 1200+ tech skills courses.