Многомерный массив создает красивую линейную структуру памяти, в то время как зубчатый массив подразумевает несколько дополнительных уровней косвенности.
Поиск значения jagged[3][6]
в зубчатом массиве var jagged = new int[10][5]
работает следующим образом: найдите элемент по индексу 3 (который является массивом) и найдите элемент по индексу 6 в этом массиве (который является значением ). Для каждого измерения в этом случае есть дополнительный поиск (это дорогой шаблон доступа к памяти).
Многомерный массив линейно размещается в памяти, фактическое значение определяется путем умножения индексов. Однако, учитывая массив var mult = new int[10,30]
, свойство Length
этого многомерного массива возвращает общее количество элементов, т.е. 10 * 30 = 300.
Свойство Rank
для зубчатого массива всегда равно 1, но многомерный массив может иметь любой ранг. Метод GetLength
любого массива можно использовать для получения длины каждого измерения. Для многомерного массива в этом примере mult.GetLength(1)
возвращает 30.
Индексирование многомерного массива происходит быстрее. например учитывая многомерный массив в этом примере mult[1,7]
= 30 * 1 + 7 = 37, получите элемент с этим индексом 37. Это лучший шаблон доступа к памяти, поскольку задействована только одна ячейка памяти, которая является базовым адресом массива .
Поэтому многомерный массив выделяет непрерывный блок памяти, в то время как зубчатый массив не должен быть квадратным, например jagged[1].Length
не должен равняться jagged[2].Length
, что было бы верно для любого многомерного массива.
Производительность
По производительности, многомерные массивы должны быть быстрее. Гораздо быстрее, но из-за очень плохой реализации 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
Первый ряд - это временные ряды, второй показывает многомерные массивы, а третий - так и должно быть. Программа показана ниже, к вашему сведению, это было протестировано в режиме моно. (Синхронизация окон сильно отличается, в основном из-за изменений в реализации CLR).
В окнах временные характеристики зубчатых массивов значительно выше, примерно так же, как моя собственная интерпретация того, на что должен быть похож многомерный массив, см. Single (). К сожалению, JIT-компилятор Windows действительно глуп, и это, к сожалению, затрудняет обсуждение производительности, слишком много несоответствий.
Это время, которое я получил на окнах, то же самое здесь, первая строка - зубчатые массивы, вторая - многомерная, и третья - моя собственная реализация многомерной, обратите внимание, насколько медленнее это для окон по сравнению с моно.
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
Исходный код:
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();
}
}