Каковы различия между многомерными массивами double[,]
и массивом массивовdouble[][]
в C #?
Если есть разница, что лучше для каждого?
Каковы различия между многомерными массивами double[,]
и массивом массивовdouble[][]
в C #?
Если есть разница, что лучше для каждого?
double[,]
представляет собой прямоугольный массив, в то времяdouble[][]
как известен как «зубчатый массив». Первый будет иметь одинаковое количество «столбцов» для каждой строки, а второй (потенциально) будет иметь разное количество «столбцов» для каждой строки.Ответы:
Массивы массивов (зубчатые массивы) работают быстрее многомерных массивов и могут использоваться более эффективно. Многомерные массивы имеют более приятный синтаксис.
Если вы напишите некоторый простой код с использованием зубчатых и многомерных массивов, а затем осмотрите скомпилированную сборку с помощью дизассемблера IL, вы увидите, что хранение и извлечение из зубчатых (или одномерных) массивов являются простыми инструкциями IL, тогда как те же операции для многомерных массивов являются методом вызовы, которые всегда медленнее.
Рассмотрим следующие методы:
Их IL будет следующим:
При использовании неровных массивов вы можете легко выполнять такие операции, как замена строк и изменение размеров строк. Может быть, в некоторых случаях использование многомерных массивов будет более безопасным, но даже Microsoft FxCop говорит, что при использовании его для анализа своих проектов следует использовать зубчатые массивы вместо многомерных.
источник
Многомерный массив создает красивую линейную структуру памяти, в то время как зубчатый массив подразумевает несколько дополнительных уровней косвенности.
Поиск значения
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 это не так.
В первом ряду приведены временные интервалы массивов, во втором - многомерные массивы, а в третьем - так и должно быть. Программа показана ниже, к вашему сведению, это было протестировано в режиме моно. (Синхронизация окон сильно отличается, в основном из-за изменений в реализации CLR).
В окнах синхронизация зубчатых массивов значительно выше, примерно так же, как моя собственная интерпретация того, на что должен быть похож внешний вид многомерного массива, см. Single (). К сожалению, JIT-компилятор Windows действительно глуп, и это, к сожалению, затрудняет обсуждение производительности, слишком много несоответствий.
Это время, которое я получил на окнах, то же самое здесь, первая строка - зубчатые массивы, вторая - многомерная и третья - моя собственная реализация многомерной, обратите внимание, насколько медленнее это для окон по сравнению с моно.
Исходный код:
источник
Проще говоря, многомерные массивы похожи на таблицы в СУБД.
Array of Array (jagged array) позволяет каждому элементу содержать другой массив того же типа переменной длины.
Итак, если вы уверены, что структура данных выглядит как таблица (фиксированные строки / столбцы), вы можете использовать многомерный массив. Зубчатый массив - это фиксированные элементы, и каждый элемент может содержать массив переменной длины
Например, Psuedocode:
Думайте об этом как о таблице 2х2:
Думайте о вышеупомянутом как о каждой строке, имеющей переменное число столбцов:
источник
Предисловие: Этот комментарий предназначен для ответа на вопрос, предоставленный okutane , но из-за глупой системы репутации SO, я не могу опубликовать его там, где он принадлежит.
Ваше утверждение, что один медленнее другого из-за вызовов метода, неверно. Один медленнее другого из-за более сложных алгоритмов проверки границ. В этом легко убедиться, посмотрев не на IL, а на скомпилированную сборку. Например, в моей версии 4.5 доступ к элементу (через указатель в edx), хранящемуся в двумерном массиве, на который указывает ecx, с индексами, хранящимися в eax и edx, выглядит так:
Здесь вы можете видеть, что нет никаких накладных расходов от вызовов методов. Проверка границ очень запутанна благодаря возможности ненулевых индексов, что является функциональностью, недоступной для зубчатых массивов. Если мы удалим sub, cmp и jmps для ненулевых случаев, код в значительной степени разрешится в
(x*y_max+y)*sizeof(ptr)+sizeof(array_header)
. Это вычисление примерно такое же быстрое (одно умножение может быть заменено сдвигом, так как это единственная причина, по которой мы выбираем байты для измерения в виде степеней двух бит), как и все остальное для произвольного доступа к элементу.Другая сложность заключается в том, что существует множество случаев, когда современный компилятор оптимизирует удаленную проверку границ для доступа к элементам при выполнении итерации по одномерному массиву. В результате получается код, который в основном просто перемещает указатель индекса по непрерывной памяти массива. Наивная итерация по многомерным массивам обычно включает дополнительный уровень вложенной логики, поэтому компилятор с меньшей вероятностью оптимизирует работу. Таким образом, даже несмотря на то, что накладные расходы на проверку границ при доступе к одному элементу амортизируются до постоянного времени выполнения в отношении размеров и размеров массива, простой тестовый пример для измерения разницы может выполняться во много раз дольше для выполнения.
источник
Я хотел бы обновить это, потому что в .NET Core многомерные массивы работают быстрее, чем зубчатые массивы . Я выполнил тесты от Джона Лейдгрена, и это результаты предварительного просмотра .NET Core 2.0 2. Я увеличил значение измерения, чтобы любые возможные влияния фоновых приложений были менее заметны.
Я посмотрел на разборки, и это то, что я нашел
jagged[i][j][k] = i * j * k;
необходимо выполнить 34 инструкцииmulti[i, j, k] = i * j * k;
необходимо выполнить 11 инструкцийsingle[i * dim * dim + j * dim + k] = i * j * k;
необходимо 23 инструкции для выполненияЯ не смог определить, почему одномерные массивы были все же быстрее многомерных, но я предполагаю, что это связано с некоторой оптимизацией, сделанной на процессоре
источник
Многомерные массивы - это (n-1) -мерные матрицы.
Так
int[,] square = new int[2,2]
же квадратная матрица 2х2,int[,,] cube = new int [3,3,3]
это куб - квадратная матрица 3х3. Пропорциональность не требуется.Зубчатые массивы - это просто массив массивов - массив, в котором каждая ячейка содержит массив.
Так что MDA пропорциональны, JD может и не быть! Каждая ячейка может содержать массив произвольной длины!
источник
Это могло быть упомянуто в ответах выше, но не в явном виде: с помощью зубчатого массива вы можете
array[row]
ссылаться на целый ряд данных, но это не разрешено для многомерных массивов.источник
В дополнение к другим ответам, обратите внимание, что многомерный массив выделяется как один большой коренастый объект в куче. Это имеет некоторые последствия:
<gcAllowVeryLargeObjects>
для многомерных массивов , как до того , как проблема будет когда - либо, если вы только когда - либо использовать неровные массивы.источник
Я разбираю файлы .il, сгенерированные ildasm, для создания базы данных сборок, классов, методов и хранимых процедур для использования при преобразовании. Я наткнулся на следующее, что сломало мой разбор.
В книге поясняется книга Expert .NET 2.0 IL Assembler, написанная Сергеем Лидином, Apress, 2006 г., глава 8, Типы примитивов и подписи, с. 149-150.
<type>[]
называется вектором<type>
,<type>[<bounds> [<bounds>**] ]
называется массивом<type>
**
средства могут повторяться,[ ]
средства необязательны.Примеры: Позвольте
<type> = int32
.1)
int32[...,...]
представляет собой двумерный массив неопределенных нижних границ и размеров2)
int32[2...5]
- одномерный массив нижней границы 2 и размера 4.3)
int32[0...,0...]
представляет собой двумерный массив с нижними границами 0 и неопределенного размера.Том
источник