Почему является настолько медленным умножение матриц в.NET?

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

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

Java vs C# vs C++ breakdown

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

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

Matrix multiplication comparison

Что продолжается здесь?

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

15
задан Glorfindel 1 August 2019 в 21:05
поделиться

4 ответа

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

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

13
ответ дан 1 December 2019 в 01:45
поделиться

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

Parallel Matrix Multiplication with the Task Parallel Library (TPL)

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

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

3
ответ дан 1 December 2019 в 01:45
поделиться

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

Во-первых, есть загвоздка для начинающих: оператор* класса XNA Matrix будет создавать несколько копий. Это медленнее, чем можно было бы ожидать от эквивалентного кода C++.

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

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

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

10
ответ дан 1 December 2019 в 01:45
поделиться

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

См. этот вопрос для более глубокого анализа того, почему многомерные массивы медленнее, чем зубчатые массивы.

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

7
ответ дан 1 December 2019 в 01:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: