Как лучше всего хранить 2D-массив в C#, чтобы оптимизировать производительность при выполнении большого количества арифметических операций с элементами в массиве?
У нас есть большие (примерно 1,5 ГБ) массивы, которые, например, мы хотим умножать друг на друга поэлементно. Производительность имеет решающее значение. Контекст, в котором это делается, находится в C#. Есть ли какой-нибудь умный способ хранить массивы и перебирать их? Можно ли написать эти части на неуправляемом C++ и действительно ли это повысит производительность? Массивы должны быть доступны для остальной части программы C#.
В настоящее время (в c) массив хранится как один длинный вектор. Мы выполняем вычисления для каждого элемента в массиве и перезаписываем старое значение. Вычисления обычно уникальны для каждого элемента вектора.
Эксперименты по времени показывают, что хранение и итерация данных в виде массива в C# медленнее, чем хранение в виде 2D-массива. Я хотел бы знать, есть ли еще лучший способ обработки данных. Конкретная выполненная арифметика не имеет отношения к вопросу.
Массив C# 2d был быстрее, чем массив 1d C#





Для лучшей производительности массива убедитесь, что вы используете одномерный массив с нижним индексом 0.
Чтобы получить доступ к элементам массива как можно быстрее, вы можете использовать небезопасные указатели, например:
int[] array = Enumerable.Range(0, 1000).ToArray();
int count = 0;
unsafe {
fixed (int* pArray = array) {
for (int i = 0; i < array.Length; i++) {
count += *(pArray + i);
}
}
}
РЕДАКТИРОВАТЬ Драт! Не заметил, что вы сказали 2D-массив. Этот трюк не сработает с многомерным массивом, поэтому я не уверен, насколько он поможет. Хотя вы можете превратить любой массив в одномерный массив, выполнив некоторую арифметику над индексом массива. Просто зависит от того, заботитесь ли вы о снижении производительности при индексировании массива или при итерации по массиву.
@Cameron MacFarland: на самом деле он может работать для 2D-массивов, вам просто нужно сделать что-то вроде * (pArray + (ii * cols) + jj).
sixlettervariables: полезно знать. Хотя это применимо только к прямоугольным массивам [,], а не к массивам с зазубринами [] [], если вы не используете счетчик столбцов для текущего массива, что замедлит работу.
Предлагаю измерить, измерить и ... хотя бы измерить! Даже если арифметика с указателями действительно приносила значительный прирост производительности в более ранних версиях JIT, оптимизация все же значительно продвинулась. К настоящему времени (.NET 4.0) преимущество кажется почти незначительным.
Анна,
Вот отличная страница, на которой обсуждается разница в производительности между традиционными научными языками программирования (fortran, C++) и C#.
http://msdn.microsoft.com/en-us/magazine/cc163995.aspx
Согласно статье C#, при использовании прямоугольных массивов (2d) может быть очень хорошим исполнителем. Вот график, который показывает разницу в производительности между зубчатыми массивами (массив массивов) и прямоугольными массивами (многомерными) массивами.
альтернативный текст http://i.msdn.microsoft.com/cc163995.fig08.gif
Я бы предложил поэкспериментировать и использовать для сравнения анализ производительности в VS 2008.
Если использование C# «достаточно быстрое», ваше приложение будет намного проще поддерживать.
Удачи!
Результаты, показанные на этом графике, взяты из .Net 1.1. Зубчатые массивы на самом деле быстрее многомерных массивов в .Net 3 и 4, как показано здесь: codeproject.com/KB/cross-platform/BenchmarkCppVsDotNet.aspx
@AndrewS - .Net 4 не был выпущен, когда я ответил на этот вопрос еще в 2008 году.
Если вы загружаете F# и ссылаетесь на одну из библиотек времени выполнения (я думаю, что это FSharp.PowerPack) и используете Microsoft.FSharp.Maths.Matrix. Он оптимизируется в зависимости от того, используете ли вы плотную или разреженную матрицу.
Вы перебираете матрицу по строкам, по столбцам или по обоим? Всегда ли вы обращаетесь к ближайшим элементам или делаете произвольный доступ к матрице.
Если в ваших доступах есть некоторая локальность, но вы не обращаетесь к ней последовательно (например, как правило, при умножении матриц), вы можете получить разницу в производительности огромный, сохранив свою матрицу более удобным для кеширования способом.
Довольно простой способ сделать это - написать небольшую функцию доступа, чтобы превратить ваши индексы строки / столбца в индекс и работать с одномерной матрицей, дружественным к кешу способом.
Функция должна группировать близлежащие координаты в близлежащие индексы. Мортон-ордер можно использовать, если вы работаете на мощности двух размеров. Для размеров без мощности часто можно привести только 4 младших бита в порядок Мортона и использовать обычную арифметику индексов для старших битов. Вы все равно получите значительное ускорение, даже если преобразование координат в индекс кажется дорогостоящей операцией.
http://en.wikipedia.org/wiki/Z-order_(curve) <- извините, не могу указать, что SO не любит URL-адреса с тире в нем. Вы должны вырезать и пасту.
Кстати, увеличение скорости в 10 и более раз вполне реально. Однако это зависит от алгоритма, который вы используете для своих матриц.
Я не совсем понимаю, что вы имеете в виду, говоря о ваших экспериментах по таймингу. Вы имеете в виду, что массив C# 2D был медленнее, чем массив C 2D?