views:

2853

answers:

4

What is differences between Multidimensional array and Array of Arrays in C#? if there a difference.
What is the best use for each one?

+18  A: 

Array of arrays (jagged arrays) is faster than multi-dimensional arrays and can be used more effectively. Multidimensional arrays have nicer syntax.

If you will write some simple code using jagged arrays and multidimensional ones and then will inspect compiled assembly with IL disassembler you will see that storage and retrieval from jagged (or single dimensional array) is simple IL instructions while same operations for multidimensional arrays is method invocations which is always slower.

Consider following methods:

static void SetElementAt(int[][] array, int i, int j, int value)
{
    array[i][j] = value;
}

static void SetElementAt(int[,] array, int i, int j, int value)
{
    array[i, j] = value;
}

Their IL will be following:

.method private hidebysig static void  SetElementAt(int32[][] 'array',
                                                    int32 i,
                                                    int32 j,
                                                    int32 'value') cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldelem.ref
  IL_0003:  ldarg.2
  IL_0004:  ldarg.3
  IL_0005:  stelem.i4
  IL_0006:  ret
} // end of method Program::SetElementAt

.method private hidebysig static void  SetElementAt(int32[0...,0...] 'array',
                                                    int32 i,
                                                    int32 j,
                                                    int32 'value') cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  ldarg.3
  IL_0004:  call       instance void int32[0...,0...]::Set(int32,
                                                           int32,
                                                           int32)
  IL_0009:  ret
} // end of method Program::SetElementAt

When using jagged arrays you can easily perform such operations as row swap and row resize. Maybe in some cases usage of multidimensional arrays will be more safe, but even Microsoft FxCop tells that jagged arrays should be used instead of multidimensional when you use it to analyse your projects.

Dmitriy Matveev
hmm. I was betting on the other way around. Can you suggest links?
jms
@Jason: http://stackoverflow.com/questions/468832/why-are-multi-dimensional-arrays-in-net-slower-than-normal-arrays
Hosam Aly
"Array of arrays (jagged arrays) is faster than multi-dimensional arrays and can be used more effectively." This is completely wrong!
John Leidegren
@John, measure them yourself, and then don't offend people because of your assumptions.
Hosam Aly
@John: My first reaction too but i was wrong - see Hosams question for details.
Henk Holterman
Multi-dimensional arrays should logically be more efficent but their implementation by the JIT compiler is not. The above code is not useful since it does not show array access in a loop.
ILoveFortran
@Henk Holterman - See my answer below, It might be the case that on windows jagged arrays are fast but one has to realize that this is entirely CLR specific and not the case with e.g. mono...
John Leidegren
+6  A: 

Simply put multidimensional arrays are similar to a table in DBMS.
Array of Array (jagged array) lets you have each element hold another array of the same type of variable length.

So, if you are sure that the structure of data looks like a table (fixed rows/columns), you can use a multi-dimensional array. Jagged array are fixed elements & each element can hold an array of variable length

e.g. Psuedocode:

int[,] data = new int[1,1];
data[0,0] = 1;
data[0,1] = 2;
data[1,0] = 3;
data[1,1] = 4;

Think of the above as a table:

1 | 2
3 | 4
int[][] jagged = new int[2][];
jagged[0] = new int[4] {  1,  2,  3,  4 };
jagged[1] = new int[2] { 11, 12 };
jagged[2] = new int[3] { 21, 22, 23 };

Think of the above as each row having variable number of columns:

 1 |  2 |  3 | 4
11 | 12
21 | 22 | 23
shahkalpesh
+13  A: 

A multidimensional array is forms a nice linear layout while a jagged array implies a several extra levels of indirection.

Looking up the value jagged[3][6] in a jagged array var jagged = new int[10][5] works like this: Look up the element at index 3 (which is an array) and look up the element at index 6 in that array (which is a value). For each dimension in this case, there's an additional look up (this is an expensive memory access pattern).

A multidimensional array is laid out linearly in memory, the actual value is found by multiplying together the indexes. However, given the array var mult = new int[10,30] the Length property of that multidimensional array returns the total number of elements i.e. 10 * 30 = 300.

The Rank property of a jagged array is always 1, but a multidimensional array can have any rank. The GetLength method of any array can be used to get the length of each dimension. For the multidimensional array in this example mult.GetLength(1) returns 30.

Indexing the multidimensional array is faster e.g. given the multidimensional array in this example mult[1,7] = 30 * 1 + 7 = 37, get the element at that index 37. This is a better memory access pattern because only one memory location is involved, which is the base address of the array.

A multidimensional array therefore allocates a continuous memory block, while a jagged array does not have to be square like. e.g. jagged[1].Length does not have to equal jagged[2].Length which would be true for any multidimensional array.

Preformance

Performance wise, multidimensional arrays should be faster. A lot faster, but due to a really bad CLR implementation they are not.

 23.084  16.634  15.215  15.489  14.407  13.691  14.695  14.398  14.551  14.252 
 25.782  27.484  25.711  20.844  19.607  20.349  25.861  26.214  19.677  20.171 
  5.050   5.085   6.412   5.225   5.100   5.751   6.650   5.222   6.770   5.305

The first row are timings of jagged arrays, the second shows multidimensional arrays and the third, well that's how it should be. The program is shown below, FYI this was tested running mono. (The windows timings are vastly different, mostly due to the CLR implementation variations).

On windows, the timings of the jagged arrays are greatly superior, about the same as my own interpretation of what multidimensional array look up should be like, see 'Single()'. Sadly the windows JIT-compiler is really stupid, and this unfortunately makes these performance discussions difficult, there are too many inconsistencies.

These are the timings I got on windows, same deal here, the first row are jagged arrays, second multidimensional and third my own implementation of multidimensional, note how much slower this is on windows compared to mono.

  8.438   2.004   8.439   4.362   4.936   4.533   4.751   4.776   4.635   5.864
  7.414  13.196  11.940  11.832  11.675  11.811  11.812  12.964  11.885  11.751
 11.355  10.788  10.527  10.541  10.745  10.723  10.651  10.930  10.639  10.595

Source code:

using System;
using System.Diagnostics;
static class ArrayPref
{
const string Format = "{0,7:0.000} ";
static void Main()
{
    Jagged();
    Multi();
    Single();
}
static void Jagged()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var jagged = new int[dim][][];
for(var i = 0; i < dim; i++)
{
    jagged[i] = new int[dim][];
    for(var j = 0; j < dim; j++)
    {
        jagged[i][j] = new int[dim];
        for(var k = 0; k < dim; k++)
        {
            jagged[i][j][k] = i * j * k;
        }
    }
}
timer.Stop();
Console.Write(Format,
    (double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
static void Multi()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var multi = new int[dim,dim,dim];
for(var i = 0; i < dim; i++)
{
    for(var j = 0; j < dim; j++)
    {
        for(var k = 0; k < dim; k++)
        {
            multi[i,j,k] = i * j * k;
        }
    }
}
timer.Stop();
Console.Write(Format,
    (double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
static void Single()
{
const int dim = 100;
for(var passes = 0; passes < 10; passes++)
{
var timer = new Stopwatch();
timer.Start();
var single = new int[dim*dim*dim];
for(var i = 0; i < dim; i++)
{
    for(var j = 0; j < dim; j++)
    {
        for(var k = 0; k < dim; k++)
        {
            single[i*dim*dim+j*dim+k] = i * j * k;
        }
    }
}
timer.Stop();
Console.Write(Format,
    (double)timer.ElapsedTicks/TimeSpan.TicksPerMillisecond);
}
Console.WriteLine();
}
}
John Leidegren
Jagged arrays: http://msdn.microsoft.com/en-us/library/2s05feca.aspx
Dmitriy Matveev
Try timing them yourself, and see how both perform. Jagged arrays are much more optimized in .NET. It may be related to bounds checking, but regardless of the reason, timings and benchmarks clearly show that jagged arrays are faster to access than multi-dimensional ones.
Hosam Aly
Did, care to weigh in, I updated my answer, I get seriously different results between mono and windows.
John Leidegren
Hosam, that's what he is saying. He is also saying, correctly, that multi-dimensional arrays SHOULD be faster, and certainly not slower than jagged arrays.
ILoveFortran
Now this is much clearer about what you mean. +1. I totally agree that multidimensional arrays *should* be faster, but as you saw yourself, reality is different (at least on Windows; I never used Mono). Sad, but true.
Hosam Aly
But your timings appear to be too small (a few milliseconds). At this level you'll have much interference from system services and/or drivers. Make your tests much larger, at least taking a second or two.
Hosam Aly
I disagree, in your your test a sizable amount of time is being spent on things not necessarily related to array indexing. I ran my test many times over and got consistent results, even if there was interference (unlikely related to services or driver IO) the results are reliable.
John Leidegren
+1  A: 

Multi-dimension arrays are (n-1)-dimension matrices.

So int[,] square = new int[2,2] is square matrix 2x2, int[,,] cube = new int [3,3,3] is a cube - square matrix 3x3. Proportionality is not required.

Jagged arrays are just array of arrays - an array where each cell contains an array.

So MDA are proportional, JD may be not! Each cell can contains an array of arbitrary length!

abatishchev