多维数组和c#数组之间有什么区别?

时间:2022-02-19 21:33:35

What are the differences between multidimensional arrays double[,] and array-of-arrays double[][] in C#?

在c#中多维数组double[,]和数组数组arrayof double[][]的区别是什么?

If there is a difference, what is the best use for each one?

如果有区别的话,对每个人来说最好的用途是什么?

9 个解决方案

#1


281  

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

数组(交错数组)比多维数组快,可以更有效地使用。多维数组具有更好的语法。

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

如果您编写一些简单的代码使用锯齿状和多维数组,然后检查编译汇编的IL反汇编程序你会看到存储和检索的锯齿状(或单维度)数组是简单的指令而多维数组相同的操作方法调用时,往往是慢。

Consider the 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 the following:

他们的IL将是:

.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.

当使用交错数组时,您可以轻松地执行诸如行交换和行调整大小之类的操作。也许在某些情况下,使用多维数组会更安全,但即使是Microsoft FxCop也指出,在使用交错数组分析项目时,应该使用交错数组,而不是多维数组。

#2


175  

A multidimensional array creates a nice linear memory layout while a jagged array implies 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).

在交错数组var jagged = new int[10][5]中查找jagged[3][6]的值如下所示:查找索引3的元素(这是一个数组),并查找该数组中索引6的元素(这是一个值)。对于本例中的每个维度,都有一个额外的查找(这是一个昂贵的内存访问模式)。

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.

在内存中线性排列多维数组,通过将索引相乘找到实际值。但是,给定数组var mult = new int[10,30],该多维数组的长度属性返回元素的总数,即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.

交错数组的秩属性总是1,但多维数组可以有任何秩。任何数组的GetLength方法都可以用来获取每个维度的长度。对于这个示例中的多维数组,mult.GetLength(1)返回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.

索引多维数组更快。例如,给定本例中的多维数组mult[1,7] = 30 * 1 + 7 = 37,则获取索引37处的元素。这是一个更好的内存访问模式,因为只涉及到一个内存位置,这是数组的基本地址。

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

因此,多维数组分配一个连续的内存块,而锯齿数组不必是方形的,例如,锯齿的[1]。长度不一定要等于锯齿形的[2]。长度,对于任何多维数组都成立。

Performance

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

性能方面,多维数组应该更快。更快,但由于CLR实现非常糟糕,所以它们不是。

 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).

第一行是交错数组的计时,第二行是多维数组,第三行是交错数组。程序如下所示,供您参考,这是在运行mono时进行的测试。(windows的运行时间差别很大,主要是由于CLR实现的变化)。

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.

在windows上,锯齿状数组的时间间隔非常优越,这与我自己对多维数组查找内容的解释类似,参见“Single()”。遗憾的是,windows jit编译器非常愚蠢,不幸的是,这使得这些性能讨论非常困难,存在太多的不一致性。

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.

这些是我在windows上得到的计时,同样的处理,第一行是交错数组,第二个多维度和第三个我自己的多维度的实现,注意这在windows上比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();
    }
}

#3


58  

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.

简单地说,多维数组与DBMS中的表类似。数组(交错数组)允许您让每个元素持有相同类型的变量长度的另一个数组。

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:

例如Psuedocode:

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

Think of the above as a 2x2 table:

把上面的内容想象成一个2x2表:

1 | 2
3 | 4
int[][] jagged = new int[3][]; 
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

#4


31  

Preface: This comment is intended to address the answer provided by okutane, but because of SO's silly reputation system, I can not post it where it belongs.

前言:这篇评论的目的是为了解决okutane提供的答案,但是由于这个愚蠢的名声系统,我不能把它贴在它的位置。

Your assertion that one is slower than the other because of the method calls isn't correct. One is slower than the other because of more complicated bounds-checking algorithms. You can easily verify this by looking, not at the IL, but at the compiled assembly. For example, on my 4.5 install, accessing an element (via pointer in edx) stored in a two-dimensional array pointed to by ecx with indexes stored in eax and edx looks like so:

由于方法调用,您断言其中一个比另一个慢,这是不正确的。其中一个比另一个慢,因为有更复杂的边界检查算法。您可以通过查看已编译的程序集而不是IL来轻松验证这一点。例如,在我的4.5安装中,通过ecx指向存储在eax和edx中的索引,访问一个元素(通过edx中的指针)存储在一个二维数组中。

sub eax,[ecx+10]
cmp eax,[ecx+08]
jae oops //jump to throw out of bounds exception
sub edx,[ecx+14]
cmp edx,[ecx+0C]
jae oops //jump to throw out of bounds exception
imul eax,[ecx+0C]
add eax,edx
lea edx,[ecx+eax*4+18]

Here, you can see that there's no overhead from method calls. The bounds checking is just very convoluted thanks to the possibility of non-zero indexes, which is a functionality not on offer with jagged arrays. If we remove the sub,cmp,and jmps for the non-zero cases, the code pretty much resolves to (x*y_max+y)*sizeof(ptr)+sizeof(array_header). This calculation is about as fast (one multiply could be replaced by a shift, since that's the whole reason we choose bytes to be sized as powers of two bits) as anything else for random access to an element.

这里,您可以看到方法调用没有开销。由于非零索引的可能性,边界检查非常复杂,这是一种不提供锯齿数组的功能。如果我们移除非零情况下的sub、cmp和jmps,代码基本上解析为(x*y_max+y)*sizeof(ptr)+sizeof(array_header)。这种计算速度几乎和其他任何对元素的随机访问一样快(一个乘可以被一个移位所取代,因为这就是为什么我们选择字节大小为两个比特的幂)。

Another complication is that there are plenty of cases where a modern compiler will optimize away the nested bounds-checking for element access while iterating over a single-dimension array. The result is code that basically just advances an index pointer over the contiguous memory of the array. Naive iteration over multi-dimensional arrays generally involves an extra layer of nested logic, so a compiler is less likely to optimize the operation. So, even though the bounds-checking overhead of accessing a single element amortizes out to constant runtime with respect to array dimensions and sizes, a simple test-case to measure the difference may take many times longer to execute.

另一个复杂的问题是,在很多情况下,现代编译器会在迭代单维数组时优化嵌套的边界——检查元素访问。其结果是,代码基本上只是在数组的连续内存上推进一个索引指针。在多维数组上的朴素迭代通常包含一个额外的嵌套逻辑层,因此编译器不太可能优化操作。因此,尽管访问单个元素的边界检查开销相对于数组的维度和大小而言会摊销到常量运行时,一个度量差异的简单测试用例可能要花很多时间才能执行。

#5


12  

I would like to update on this, because in .NET Core multi-dimensional arrays are faster than jagged arrays. I ran the tests from John Leidegren and these are the results on .NET Core 2.0 preview 2. I increased the dimension value to make any possible influences from background apps less visible.

我想对此进行更新,因为在。net Core多维数组中,多维数组的速度比交错数组快。我运行了John Leidegren的测试,这些是。net Core 2.0预览2的结果。我增加了维度值,以使来自后台应用程序的任何可能的影响变得不那么明显。

Debug (code optimalization disabled)
Running jagged 
187.232 200.585 219.927 227.765 225.334 222.745 224.036 222.396 219.912 222.737 

Running multi-dimensional  
130.732 151.398 131.763 129.740 129.572 159.948 145.464 131.930 133.117 129.342 

Running single-dimensional  
 91.153 145.657 111.974  96.436 100.015  97.640  94.581 139.658 108.326  92.931 


Release (code optimalization enabled)
Running jagged 
108.503 95.409 128.187 121.877 119.295 118.201 102.321 116.393 125.499 116.459 

Running multi-dimensional 
 62.292  60.627  60.611  60.883  61.167  60.923  62.083  60.932  61.444  62.974 

Running single-dimensional 
 34.974  33.901  34.088  34.659  34.064  34.735  34.919  34.694  35.006  34.796 

I looked into disassemblies and this is what I found

我研究了分离,这就是我发现的

jagged[i][j][k] = i * j * k; needed 34 instructions to execute

锯齿状[i][k] = i * j * k;需要34条指令才能执行

multi[i, j, k] = i * j * k; needed 11 instructions to execute

多[i, j, k] = i * j * k;需要11条指令才能执行

single[i * dim * dim + j * dim + k] = i * j * k; needed 23 instructions to execute

单项[i * dim * dim + j * dim + k] = i * j * k;需要23条指令才能执行

I wasn't able to identify why single-dimensional arrays were still faster than multi-dimensional but my guess is that it has to do with some optimalization made on the CPU

我无法确定为什么单维数组仍然比多维数组快,但我猜测这与CPU上的优化有关

#6


11  

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

多维数组是(n-1)维矩阵。

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.

所以int[,] square = new int[2,2]是方阵2x2, int[,] cube = new int[3,3,3]是方阵3x3。比例不是必需的。

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!

所以MDA是成比例的,JD可能不是!每个单元格可以包含任意长度的数组!

#7


5  

This might have been mentioned in the above answers but not explicitly: with jagged array you can use array[row] to refer a whole row of data, but this is not allowed for multi-d arrays.

上面的答案中可能已经提到了这一点,但没有明确说明:对于交错数组,您可以使用array[row]来引用整行数据,但这对于多d数组是不允许的。

#8


1  

In addition to the other answers, note that a multidimensional array is allocated as one big chunky object on the heap. This has some implications:

除了其他答案之外,请注意多维数组被分配为堆上的一个大而粗的对象。这有一些影响:

  1. Some multidimensional arrays will get allocated on the Large Object Heap (LOH) where their equivalent jagged array counterparts would otherwise not have.
  2. 一些多维数组将被分配到大型对象堆(LOH)上,否则,它们的对等交错数组将没有。
  3. The GC will need to find a single contiguous free block of memory to allocate a multidimensional array, whereas a jagged array might be able to fill in gaps caused by heap fragmentation... this isn't usually an issue in .NET because of compaction, but the LOH doesn't get compacted by default (you have to ask for it, and you have to ask every time you want it).
  4. GC将需要找到一个连续的空闲内存块来分配多维数组,而交错数组可能能够填补堆碎片造成的空白……这在。net中通常不是因为压缩而引起的问题,但是LOH在默认情况下不会被压缩(您必须要求它,而且每次需要时都必须问)。
  5. You'll want to look into <gcAllowVeryLargeObjects> for multidimensional arrays way before the issue will ever come up if you only ever use jagged arrays.
  6. 如果您只使用交错数组,那么在问题出现之前,您需要查看 用于多维数组。

#9


0  

I am parsing .il files generated by ildasm to build a database of assemnblies, classes, methods, and stored procedures for use doing a conversion. I came across the following, which broke my parsing.

我正在解析由ildasm生成的.il文件,以构建一个用于进行转换的汇编、类、方法和存储过程的数据库。我遇到了下面的问题,这打破了我的分析。

.method private hidebysig instance uint32[0...,0...] 
        GenerateWorkingKey(uint8[] key,
                           bool forEncryption) cil managed

The book Expert .NET 2.0 IL Assembler, by Serge Lidin, Apress, published 2006, Chapter 8, Primitive Types and Signatures, pp. 149-150 explains.

本书专家。net 2.0 IL汇编,Serge Lidin, Apress, 2006年出版,第8章,原始类型和签名,第149-150页解释。

<type>[] is termed a Vector of <type>,

[]被称为< >的向量,

<type>[<bounds> [<bounds>**] ] is termed an array of <type>

[ [ *]]]被称为 的数组

** means may be repeated, [ ] means optional.

**表示可以重复,[]表示可选。

Examples: Let <type> = int32.

示例:让 = int32。

1) int32[...,...] is a two-dimensional array of undefined lower bounds and sizes

1)int32[……是一个二维数组,其下界和大小未定义

2) int32[2...5] is a one-dimensional array of lower bound 2 and size 4.

2)int32[2…5]是下界2和尺寸4的一维数组。

3) int32[0...,0...] is a two-dimensional array of lower bounds 0 and undefined size.

3)int32[0,0…是一个下界0和未定义大小的二维数组。

Tom

汤姆

#1


281  

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

数组(交错数组)比多维数组快,可以更有效地使用。多维数组具有更好的语法。

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

如果您编写一些简单的代码使用锯齿状和多维数组,然后检查编译汇编的IL反汇编程序你会看到存储和检索的锯齿状(或单维度)数组是简单的指令而多维数组相同的操作方法调用时,往往是慢。

Consider the 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 the following:

他们的IL将是:

.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.

当使用交错数组时,您可以轻松地执行诸如行交换和行调整大小之类的操作。也许在某些情况下,使用多维数组会更安全,但即使是Microsoft FxCop也指出,在使用交错数组分析项目时,应该使用交错数组,而不是多维数组。

#2


175  

A multidimensional array creates a nice linear memory layout while a jagged array implies 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).

在交错数组var jagged = new int[10][5]中查找jagged[3][6]的值如下所示:查找索引3的元素(这是一个数组),并查找该数组中索引6的元素(这是一个值)。对于本例中的每个维度,都有一个额外的查找(这是一个昂贵的内存访问模式)。

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.

在内存中线性排列多维数组,通过将索引相乘找到实际值。但是,给定数组var mult = new int[10,30],该多维数组的长度属性返回元素的总数,即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.

交错数组的秩属性总是1,但多维数组可以有任何秩。任何数组的GetLength方法都可以用来获取每个维度的长度。对于这个示例中的多维数组,mult.GetLength(1)返回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.

索引多维数组更快。例如,给定本例中的多维数组mult[1,7] = 30 * 1 + 7 = 37,则获取索引37处的元素。这是一个更好的内存访问模式,因为只涉及到一个内存位置,这是数组的基本地址。

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

因此,多维数组分配一个连续的内存块,而锯齿数组不必是方形的,例如,锯齿的[1]。长度不一定要等于锯齿形的[2]。长度,对于任何多维数组都成立。

Performance

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

性能方面,多维数组应该更快。更快,但由于CLR实现非常糟糕,所以它们不是。

 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).

第一行是交错数组的计时,第二行是多维数组,第三行是交错数组。程序如下所示,供您参考,这是在运行mono时进行的测试。(windows的运行时间差别很大,主要是由于CLR实现的变化)。

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.

在windows上,锯齿状数组的时间间隔非常优越,这与我自己对多维数组查找内容的解释类似,参见“Single()”。遗憾的是,windows jit编译器非常愚蠢,不幸的是,这使得这些性能讨论非常困难,存在太多的不一致性。

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.

这些是我在windows上得到的计时,同样的处理,第一行是交错数组,第二个多维度和第三个我自己的多维度的实现,注意这在windows上比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();
    }
}

#3


58  

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.

简单地说,多维数组与DBMS中的表类似。数组(交错数组)允许您让每个元素持有相同类型的变量长度的另一个数组。

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:

例如Psuedocode:

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

Think of the above as a 2x2 table:

把上面的内容想象成一个2x2表:

1 | 2
3 | 4
int[][] jagged = new int[3][]; 
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

#4


31  

Preface: This comment is intended to address the answer provided by okutane, but because of SO's silly reputation system, I can not post it where it belongs.

前言:这篇评论的目的是为了解决okutane提供的答案,但是由于这个愚蠢的名声系统,我不能把它贴在它的位置。

Your assertion that one is slower than the other because of the method calls isn't correct. One is slower than the other because of more complicated bounds-checking algorithms. You can easily verify this by looking, not at the IL, but at the compiled assembly. For example, on my 4.5 install, accessing an element (via pointer in edx) stored in a two-dimensional array pointed to by ecx with indexes stored in eax and edx looks like so:

由于方法调用,您断言其中一个比另一个慢,这是不正确的。其中一个比另一个慢,因为有更复杂的边界检查算法。您可以通过查看已编译的程序集而不是IL来轻松验证这一点。例如,在我的4.5安装中,通过ecx指向存储在eax和edx中的索引,访问一个元素(通过edx中的指针)存储在一个二维数组中。

sub eax,[ecx+10]
cmp eax,[ecx+08]
jae oops //jump to throw out of bounds exception
sub edx,[ecx+14]
cmp edx,[ecx+0C]
jae oops //jump to throw out of bounds exception
imul eax,[ecx+0C]
add eax,edx
lea edx,[ecx+eax*4+18]

Here, you can see that there's no overhead from method calls. The bounds checking is just very convoluted thanks to the possibility of non-zero indexes, which is a functionality not on offer with jagged arrays. If we remove the sub,cmp,and jmps for the non-zero cases, the code pretty much resolves to (x*y_max+y)*sizeof(ptr)+sizeof(array_header). This calculation is about as fast (one multiply could be replaced by a shift, since that's the whole reason we choose bytes to be sized as powers of two bits) as anything else for random access to an element.

这里,您可以看到方法调用没有开销。由于非零索引的可能性,边界检查非常复杂,这是一种不提供锯齿数组的功能。如果我们移除非零情况下的sub、cmp和jmps,代码基本上解析为(x*y_max+y)*sizeof(ptr)+sizeof(array_header)。这种计算速度几乎和其他任何对元素的随机访问一样快(一个乘可以被一个移位所取代,因为这就是为什么我们选择字节大小为两个比特的幂)。

Another complication is that there are plenty of cases where a modern compiler will optimize away the nested bounds-checking for element access while iterating over a single-dimension array. The result is code that basically just advances an index pointer over the contiguous memory of the array. Naive iteration over multi-dimensional arrays generally involves an extra layer of nested logic, so a compiler is less likely to optimize the operation. So, even though the bounds-checking overhead of accessing a single element amortizes out to constant runtime with respect to array dimensions and sizes, a simple test-case to measure the difference may take many times longer to execute.

另一个复杂的问题是,在很多情况下,现代编译器会在迭代单维数组时优化嵌套的边界——检查元素访问。其结果是,代码基本上只是在数组的连续内存上推进一个索引指针。在多维数组上的朴素迭代通常包含一个额外的嵌套逻辑层,因此编译器不太可能优化操作。因此,尽管访问单个元素的边界检查开销相对于数组的维度和大小而言会摊销到常量运行时,一个度量差异的简单测试用例可能要花很多时间才能执行。

#5


12  

I would like to update on this, because in .NET Core multi-dimensional arrays are faster than jagged arrays. I ran the tests from John Leidegren and these are the results on .NET Core 2.0 preview 2. I increased the dimension value to make any possible influences from background apps less visible.

我想对此进行更新,因为在。net Core多维数组中,多维数组的速度比交错数组快。我运行了John Leidegren的测试,这些是。net Core 2.0预览2的结果。我增加了维度值,以使来自后台应用程序的任何可能的影响变得不那么明显。

Debug (code optimalization disabled)
Running jagged 
187.232 200.585 219.927 227.765 225.334 222.745 224.036 222.396 219.912 222.737 

Running multi-dimensional  
130.732 151.398 131.763 129.740 129.572 159.948 145.464 131.930 133.117 129.342 

Running single-dimensional  
 91.153 145.657 111.974  96.436 100.015  97.640  94.581 139.658 108.326  92.931 


Release (code optimalization enabled)
Running jagged 
108.503 95.409 128.187 121.877 119.295 118.201 102.321 116.393 125.499 116.459 

Running multi-dimensional 
 62.292  60.627  60.611  60.883  61.167  60.923  62.083  60.932  61.444  62.974 

Running single-dimensional 
 34.974  33.901  34.088  34.659  34.064  34.735  34.919  34.694  35.006  34.796 

I looked into disassemblies and this is what I found

我研究了分离,这就是我发现的

jagged[i][j][k] = i * j * k; needed 34 instructions to execute

锯齿状[i][k] = i * j * k;需要34条指令才能执行

multi[i, j, k] = i * j * k; needed 11 instructions to execute

多[i, j, k] = i * j * k;需要11条指令才能执行

single[i * dim * dim + j * dim + k] = i * j * k; needed 23 instructions to execute

单项[i * dim * dim + j * dim + k] = i * j * k;需要23条指令才能执行

I wasn't able to identify why single-dimensional arrays were still faster than multi-dimensional but my guess is that it has to do with some optimalization made on the CPU

我无法确定为什么单维数组仍然比多维数组快,但我猜测这与CPU上的优化有关

#6


11  

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

多维数组是(n-1)维矩阵。

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.

所以int[,] square = new int[2,2]是方阵2x2, int[,] cube = new int[3,3,3]是方阵3x3。比例不是必需的。

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!

所以MDA是成比例的,JD可能不是!每个单元格可以包含任意长度的数组!

#7


5  

This might have been mentioned in the above answers but not explicitly: with jagged array you can use array[row] to refer a whole row of data, but this is not allowed for multi-d arrays.

上面的答案中可能已经提到了这一点,但没有明确说明:对于交错数组,您可以使用array[row]来引用整行数据,但这对于多d数组是不允许的。

#8


1  

In addition to the other answers, note that a multidimensional array is allocated as one big chunky object on the heap. This has some implications:

除了其他答案之外,请注意多维数组被分配为堆上的一个大而粗的对象。这有一些影响:

  1. Some multidimensional arrays will get allocated on the Large Object Heap (LOH) where their equivalent jagged array counterparts would otherwise not have.
  2. 一些多维数组将被分配到大型对象堆(LOH)上,否则,它们的对等交错数组将没有。
  3. The GC will need to find a single contiguous free block of memory to allocate a multidimensional array, whereas a jagged array might be able to fill in gaps caused by heap fragmentation... this isn't usually an issue in .NET because of compaction, but the LOH doesn't get compacted by default (you have to ask for it, and you have to ask every time you want it).
  4. GC将需要找到一个连续的空闲内存块来分配多维数组,而交错数组可能能够填补堆碎片造成的空白……这在。net中通常不是因为压缩而引起的问题,但是LOH在默认情况下不会被压缩(您必须要求它,而且每次需要时都必须问)。
  5. You'll want to look into <gcAllowVeryLargeObjects> for multidimensional arrays way before the issue will ever come up if you only ever use jagged arrays.
  6. 如果您只使用交错数组,那么在问题出现之前,您需要查看 用于多维数组。

#9


0  

I am parsing .il files generated by ildasm to build a database of assemnblies, classes, methods, and stored procedures for use doing a conversion. I came across the following, which broke my parsing.

我正在解析由ildasm生成的.il文件,以构建一个用于进行转换的汇编、类、方法和存储过程的数据库。我遇到了下面的问题,这打破了我的分析。

.method private hidebysig instance uint32[0...,0...] 
        GenerateWorkingKey(uint8[] key,
                           bool forEncryption) cil managed

The book Expert .NET 2.0 IL Assembler, by Serge Lidin, Apress, published 2006, Chapter 8, Primitive Types and Signatures, pp. 149-150 explains.

本书专家。net 2.0 IL汇编,Serge Lidin, Apress, 2006年出版,第8章,原始类型和签名,第149-150页解释。

<type>[] is termed a Vector of <type>,

[]被称为< >的向量,

<type>[<bounds> [<bounds>**] ] is termed an array of <type>

[ [ *]]]被称为 的数组

** means may be repeated, [ ] means optional.

**表示可以重复,[]表示可选。

Examples: Let <type> = int32.

示例:让 = int32。

1) int32[...,...] is a two-dimensional array of undefined lower bounds and sizes

1)int32[……是一个二维数组,其下界和大小未定义

2) int32[2...5] is a one-dimensional array of lower bound 2 and size 4.

2)int32[2…5]是下界2和尺寸4的一维数组。

3) int32[0...,0...] is a two-dimensional array of lower bounds 0 and undefined size.

3)int32[0,0…是一个下界0和未定义大小的二维数组。

Tom

汤姆