Почему умножение матриц в .NET так медленно? - PullRequest
15 голосов
/ 12 июля 2010

Я не совсем понимаю, что делает умножение матриц в C # / .NET (и даже Java) таким медленным.

Посмотрите на этот тест ( источник ): Пытается найти обновленный тест.

Java против C # против C ++ с разбивкой http://img411.imageshack.us/img411/9324/perf.gif

Целочисленная и двойная производительность C # чертовски близка к C ++, скомпилированному с MSVC ++. 87% быстрее для двойного и 99% быстрее для 32-разрядного целого числа. Довольно чертовски хорошо, я бы сказал. Но тогда посмотрите на умножение матриц. Разрыв в C # увеличивается примерно на 19%. Это довольно большое несоответствие, которое я не понимаю. Умножение матриц - это просто математика. Как это так медленно? Разве это не должно быть примерно так же быстро, как эквивалентное число простых операций с плавающей точкой или целочисленных операций?

Это особенно касается игр и XNA, где производительность матрицы и вектора важна для таких вещей, как физические движки. Некоторое время назад Mono добавил поддержку SIMD-инструкций через несколько классных векторных и матричных классов. Это сокращает разрыв и делает Mono быстрее, чем рукописный C ++, хотя и не так быстро, как C ++ с SIMD. ( источник )

Сравнение умножения матриц http://img237.imageshack.us/img237/2788/resultse.png

Что здесь происходит?

Редактировать: Если присмотреться, я неправильно прочитал второй график. C # выглядит довольно близко. Первый тест просто делает что-то ужасно неправильное? Извините, я пропустил номер версии в первом тесте. Я взял это как удобный справочник для "C # линейная алгебра медленная", которую я всегда слышал. Я постараюсь найти другую.

Ответы [ 4 ]

13 голосов
/ 12 июля 2010

При больших матрицах, подобных этой, кэш ЦП становится ограничивающим фактором. Что очень важно, так это то, как хранится матрица. И эталонный код сравнивает яблоки и апельсины. В коде C ++ используются зубчатые массивы, в коде C # используются двумерные массивы.

Переписывание кода C # для использования зубчатых массивов также удвоило его скорость. Переписывать код умножения матрицы, чтобы избежать проверки границы индекса массива, казалось бессмысленным, никто не использовал бы такой код для реальных проблем.

10 голосов
/ 12 июля 2010

Чтобы объяснить происхождение идеи, что матричные операции XNA медленны:

Прежде всего, есть ошибка начального уровня: класс XNA Matrix класса operator* сделает несколько копий. Это медленнее, чем вы можете ожидать от эквивалентного кода C ++.

(Конечно, если вы используете Matrix.Multiply(), то можете перейти по ссылке.)

Вторая причина заключается в том, что .NET Compact Framework, используемый XNA на Xbox 360, не имеет доступа к оборудованию VMX (SIMD), доступному для собственных игр C ++.

Вот почему вы продолжаете слышать, что это, по крайней мере, медленно. Как вы можете видеть из тестов, которые вы опубликовали - это не так уж и медленно, если сравнивать яблоки с яблоками.

7 голосов
/ 12 июля 2010

Хорошо, автор теста не понимает разницы между зубчатыми и многомерными массивами в C #.Это действительно было не яблоки с яблоками для сравнения.Когда я изменил код, чтобы использовать неровные массивы вместо многомерных массивов, чтобы он работал более сходным с Java образом, тогда код C # работает в два раза быстрее ... делая его быстрее, чем Java (хотя и едва ли, и это вероятностатистически незначимый).В C # многомерные массивы работают медленнее, потому что при поиске слота массива требуется дополнительная работа, а также потому, что проверка границ массива не может быть для них исключена ... пока.более глубокий анализ того, почему многомерные массивы медленнее, чем зубчатые массивы.

См. этот блог для получения дополнительной информации о проверке границ массива.В статье особо предостерегает от использования многомерных массивов для умножения матриц.

3 голосов
/ 12 июля 2010

Вот обновленный бенчмарк, касающийся умножения матриц (и некоторых бенчмарков, использующих новую параллельную библиотеку задач):

Умножение параллельных матриц с параллельной библиотекой задач (TPL)

В статье рассматриваются различные методы и объясняется, почему многомерные массивы являются плохим выбором:

Самый простой способ сделать матрицу умножение с .NET многомерный массив с i, j, k упорядочение в петлях. Проблемы являются двойными. Во-первых, я, J.K заказа обращается к памяти в беспокойном режиме вызывая данные в разных местах, чтобы быть втянул. Во-вторых, он использует многомерный массив. Да, .NET многомерный массив удобен, но это очень медленно.

...