Skip to content

Array

This page will look into arrays and their usage.

Prefer jagged arrays over multidimensional

In a multidimensional array, each element in each dimension has the same, fixed size as the other elements in that dimension. In a jagged array, which is an array of arrays, each inner array can be of a different size. By only using the space that’s needed for a given array, no space is wasted. Also from a performance point of view a jagged array performs generally better than a multidimensional array.

Bad Multidimensional array performs less optimal and can waste space.

int[,] multiDimArray = { {1, 2}, {3, 4}};

Good Jagged array performs generally better and only uses the really needed space.

int[][] jaggedArray = { new int[] {1, 2}, new int [] {3, 4}};

Benchmark

[MemoryDiagnoser]
public class Arrays
{
    [Params(25, 100)]
    public int Size { get; set; }

    [Benchmark]
    public int[][] CreateAndFillJaggedArray()
    {
        int[][] jaggedArray = new int[Size][];
        for (var i = 0; i < Size; i++)
        {
            jaggedArray[i] = new int[Size];
            for (var j = 0; j < Size; j++) jaggedArray[i][j] = i + j;
        }

        return jaggedArray;
    }

    [Benchmark]
    public int[,] CreateAndFillMultidimensionalArray()
    {
        int[,] multidimArray = new int[Size, Size];
        for (var i = 0; i < Size; i++)
        {
            for (var j = 0; j < Size; j++) 
                multidimArray[i, j] = i + j;
        }

        return multidimArray;
    }
}

Result:

|                             Method | Size |        Mean |     Error |    StdDev |      Median |   Gen 0 |  Gen 1 | Allocated |
|----------------------------------- |----- |------------:|----------:|----------:|------------:|--------:|-------:|----------:|
|           CreateAndFillJaggedArray |   25 |    717.7 ns |  15.51 ns |  43.50 ns |    700.3 ns |  0.8183 |      - |   3.34 KB |
| CreateAndFillMultidimensionalArray |   25 |  1,480.2 ns |  29.49 ns |  82.21 ns |  1,463.7 ns |  0.6065 |      - |   2.48 KB |
|           CreateAndFillJaggedArray |  100 |  9,895.6 ns | 143.22 ns | 126.96 ns |  9,900.8 ns | 10.3302 | 0.0153 |  42.21 KB |
| CreateAndFillMultidimensionalArray |  100 | 16,374.7 ns | 319.00 ns | 567.02 ns | 16,311.2 ns |  9.5215 | 0.0305 |   39.1 KB |

Locality matters

Memory location matters. Accessing memory location sequentially as opposed to “jumping” around to read some memory address.

Bad Do not access memory sequentially.

public int NotLocal()
{
    var sum = 0;
    for (var i = 0; i < _table.GetLength(1); i++)
    {
        for (var j = 0; j < _table.GetLength(0); j++) 
            sum += _table[j, i];
    }

    return sum;
}

Good Accessing memory sequentially.

public int Local()
{
    var sum = 0;
    for (var i = 0; i < _table.GetLength(0); i++)
    {
        for (var j = 0; j < _table.GetLength(1); j++) 
            sum += _table[i, j];
    }

    return sum;
}

Benchmark

Results for the given example above:

|   Method |     Mean |   Error |  StdDev | Ratio | RatioSD |
|--------- |---------:|--------:|--------:|------:|--------:|
|    Local | 120.8 us | 2.35 us | 2.70 us |  1.00 |    0.00 |
| NotLocal | 158.6 us | 3.09 us | 3.56 us |  1.31 |    0.05 |