Skip to content

skepee-LAB/MutableVsImmutable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mutable vs. Immutable

Introduction

Mutable and immutable types are concepts regarding not only an unchangable value (like constant or readonly) but also about memory allocation. This work will show an example with documented screenshots the concept that is under the hood.

Use case: concatenation algorithm

A simple code example will help to understand practically when mutable or immutable types can be used. As a simple example let's consider a string concatenation algorithm starting from a list of words. Why choosing concatenation? First of all because concatenation is a very common topic in programming and then it helps to understand what happens in terms of memory allocation. It is useful to know that as example the list of words that start with the following sequence:

abbey
absent
absolute
abstract
accessible
activate
active
...

and now let's implement a concatenation algorithm by using mutable and immutable types separately. But, before that we need to set something in Visual Studio...

Use the Memory windows in the Visual Studio debugger

Visual Studio offers some tools to check memory allocation in Debug Mode. One of these is Memory windows, for example, that enables address-level debugging by selecting Tools > Options (or Debug > Options) > Debugging > General.

Start debugging by selecting the green arrow, pressing F5, or selecting Debug > Start Debugging. Under Debug > Windows > Memory, select Memory 1, Memory 2, Memory 3, or Memory 4. (Some editions of Visual Studio offer only one Memory window.) More info here

Concatenation through immutable types

Let's consider a basic algorithm of concatenation by using immutable types. As a string resultImmutable is a immutable type. On each iteration it results that resultImmutable has a longer string by concatenating each element of the array.

private static string ConcatenateWordsImmutable(string[] words)
{
    string resultImmutable = string.Empty;

    foreach (string item in words)
    {
	resultImmutable += item + ",";
    }

    return resultImmutable;
}

Iteration 1:

The memory window shows the memory address and allocation for the variables used at that time of running code. Let's put our variable resultImmutable in the address box in top left corner and it will show the memory allocation at that moment. The left side is the address memory, then a rappresentation of the hexadecimal code and finally the value that corresponds to abbey, as expected.

On the first iteration we will have this situation: Iteration 1: value "abbey,", memory address: 0x0000019780019788

Immutable_2a


Iteration 2:

the following screenshot shows the memory allocation in the next iteration.

Immutable_2b

On the second iteration we will have this situation: Iteration 2: value "abbey, absent," memory address: 0x00000197800197B0


Iteration 500:

Immutable_2c

Iteration 500: value "abbey, absent, absolute, ..." memory address: 0x0000019780201DB0

Recap after all iterations (immutable):

As we can see at each iteration resultImmutable value changes (obviously) in line with the memory allocation.

Concatenation through mutable types

Now let us consider concatenation through mutable type. The concatenation algorithm is basically the same, apart that our variable resultMutable is StringBuilder, mutable type.

private static string ConcatenateWordsMutable(string[] words)
{
    var resultMutable = new StringBuilder();

    foreach (string item in words)
    {
	resultMutable.Append(item).Append(",");
    }

    return resultMutable.ToString();
}

With the same logic, the following screenshots show the same as below for mutable iteration.

Iteration 1:

Mutable_2a


Iteration 2:

Mutable_2b


Iteration 500:

Mutable_2c

Recap after all iterations (mutable):

As we can see at each iteration resultImmutable value changes (obviously) but the the memory address remains the same. On each iteration the memory address 0x0000019780203C18 does not change, but obviously the content changes (we are concatenating a string each time) and more memory will be allocated but always starting from the same initial memory address.

Recap after all iterations:

This summaries the result of both simulations:

Iteration Value Address Memory Mutable Address Memory Immutable
1 abbey 0x0000019780203C18 0x0000019780019788
2 abbey, absent 0x0000019780203C18 0x00000197800197B0
500 abbey, absent, ... 0x0000019780203C18 0x0000019780201DB0

A look at the performance

What about if we need to concatenate a long list of strings? The following figures show the elaboration time by using immutable (String) and mutable (StringBuilder) type against a number of words.

ImmutableGraph

MutableGraph

As we can see mutable type has more performance, for mutable type the elaboration time when the list is very long tends to be exponential (look at the trend line). At the moment when I am writing this work the iteration for 5M words for mutable is still going ... On the other side by using mutable types the elaboration time is very fast. Just to give you an idea by reading the graph, in a quarter of sec it works 5M iterations of concatenations when by using mutable type it's not yet finished!!!! Obviously the performance could be better by using async calls for example. The purpose of this work is not about perfomance but to give an idea of what happens when you use mutable or immutable types.

BenchamarkDotNet

This the benchmark by using BenchamarkDotNet:

Method Mean Error StdDev Gen0 Gen1 Allocated
MutableType 5.888 us 0.1170 us 0.1889 us 2.6245 0.1526 16.09 KB
ImmutableType 135.286 us 3.2935 us 9.7109 us 316.1621 15.0146 1937.41 KB

Final considerations

This work shows what happens when you work with mutable or immutables types.

The purpose of this work is not about perfomance but to give an idea of what happens when you use mutable or immutable types by using the same algorithm. This work shows under the hood how CLR handles memory allocation for mutable and immutable types.

As an example the same concatenation algorithm (very basic) has been used by using String as immutable type and StringBuilder as mutable type. The figures show that by increasing the number of strings to concatenate the elaboration time has a linear trend line for mutable and exponenetial for immutable.

So, when you need to work with string concatenation, very common task in programming, this work suggests to use mutable type like StringBuilder and not immutable type.

Hope it helps!