Скорость .NET в числовых вычислениях - PullRequest
17 голосов
/ 02 декабря 2009

По моему опыту, .NET в 2–3 раза медленнее, чем собственный код. (Я реализовал L-BFGS для многомерной оптимизации).

Я проследил рекламу в стеке http://www.centerspace.net/products/

скорость действительно потрясающая, скорость близка к нативному коду. Как они могут это сделать? Они сказали, что:

Q. Является ли NMath "чистым" .NET?

а. Ответ в некоторой степени зависит от вашего определения «чистого .NET». NMath написан на C #, плюс небольшой слой Managed C ++. Однако для повышения производительности основных операций линейной алгебры NMath полагается на встроенную библиотеку ядра Intel Math (включенную в NMath). Но нет никаких компонентов COM, нет DLL - только сборки .NET. Кроме того, вся память, выделенная на уровне Managed C ++ и используемая собственным кодом, выделяется из управляемой кучи.

Может кто-нибудь объяснить мне больше?

Ответы [ 8 ]

11 голосов
/ 02 июля 2010

Как они могут это сделать?

Как и большинство числовых библиотек для .NET, NMath - это всего лишь оболочка над Intel MKL, встроенным в сборку .NET, возможно, путем соединения с C ++ / CLI для создания смешанной сборки . Вы, вероятно, только что протестировали те биты, которые на самом деле не написаны в .NET.

Статьи в журнале F # .NET Числовые библиотеки: специальные функции, интерполяции и случайные числа (16 марта 2008 г.) и Числовые библиотеки: линейная алгебра и спектральные методы (16 апреля 2008 г.) протестировано немало функциональных возможностей, и NMath был самой медленной из всех коммерческих библиотек. Их PRNG был медленнее, чем все остальные, и на 50% медленнее, чем бесплатная библиотека Math.NET, некоторые базовые функции отсутствовали (например, возможность вычисления Gamma(-0.5)), а другие базовые функции (функции, связанные с гамма-связью, которые они предоставили) были нарушены , И Extreme Optimization, и Bluebit опередили NMath в тесте eigensolver. NMath даже не обеспечил преобразование Фурье в то время.

Еще более удивительно, что расхождения в производительности иногда были огромными. Самая дорогая коммерческая числовая библиотека, которую мы тестировали (IMSL), была в 500 раз медленнее, чем бесплатная библиотека FFTW в тесте FFT, и ни одна библиотек не использовала несколько ядер в то время.

На самом деле, именно плохое качество этих библиотек побудило нас коммерциализировать нашу собственную библиотеку F # для чисел (которая на 100% является чистым кодом F #).

10 голосов
/ 17 февраля 2012

Я один из ведущих разработчиков ILNumerics . Так что я предвзят, очевидно;) Но мы более раскрыты в отношении наших внутренних функций, поэтому я расскажу о наших скоростных «секретах».

Все зависит от того, как используются системные ресурсы! Если вы работаете на чистой скорости и вам нужно работать с большими массивами, вы обязательно (упорядочены по важности, в первую очередь по важности)

  1. Управляйте своей памятью соответствующим образом! «Наивное» управление памятью приведет к снижению производительности, так как оно сильно влияет на сборщик мусора, вызывает фрагментацию памяти и ухудшает локальность памяти (следовательно, производительность кэша). В среде с сборкой мусора, такой как .NET, это сводится к предотвращению частого выделения памяти. В ILNumerics мы реализовали высокопроизводительный пул памяти для достижения этой цели (и детерминированного избавления от временных массивов, чтобы получить красивый, удобный синтаксис без неуклюжей семантики функций).

  2. Используйте параллелизм! Это касается как параллелизма на уровне потоков, так и параллелизма на уровне данных. Многоядерные процессоры используются для многопоточности вычислений. На процессорах X86 / X64 SIMD / мультимедийные расширения, такие как SSE.XX и AVX, позволяют небольшую, но эффективную векторизацию. Они не имеют прямого обращения к текущим языкам .NET. И это единственная причина, почему MKL все еще быстрее, чем «чистый» код .NET. (Но решения уже растут.)

  3. Для архивирования скорости высокооптимизированных языков , таких как FORTRAN и C ++, к вашему коду должны применяться те же оптимизации, что и для них. C # предлагает возможность сделать это.

Обратите внимание, эти меры предосторожности должны соблюдаться в указанном порядке! Не имеет смысла заботиться о расширениях SSE или даже об удалении связанных проверок, если узким местом является пропускная способность памяти, а процессор (ы) большую часть времени тратит на ожидание новых данных. Кроме того, для многих простых операций даже не стоит вкладывать огромные усилия в архивирование самого последнего крошечного масштаба до максимальной производительности! Рассмотрим общий пример функции LAPACK DAXPY. Он добавляет элементы вектора X к соответствующему элементу другого вектора Y. Если это делается впервые, вся память для X и Y должна быть извлечена из основной памяти. Вы ничего не можете с этим поделать. И память является узким местом! Поэтому независимо от того, делается ли сложение в конце наивным способом в C #

for (int i = 0; i < C.Length; i++) {
    C[i] = X[i] + Y[i]; 
}

или сделано с помощью векторизационных стратегий - придется ждать памяти!

Я знаю, что этот ответ каким-то образом «переоценивает» вопрос, так как большинство из этих стратегий в настоящее время не используются из упомянутого продукта (пока?). Следуя этим пунктам, вы в конечном итоге получите гораздо лучшую производительность, чем каждая наивная реализация на «родном» языке.

Если вам интересно, можете ли вы рассказать о своей реализации L-BFGS? Я буду рад преобразовать его в ILNumerics и опубликовать результаты сравнения, и я уверен, что другие библиотеки, перечисленные здесь, хотели бы следовать. (?)

8 голосов
/ 02 декабря 2009

Правильно сказано о C ++ / CLI. Чтобы завершить картину, достаточно двух дополнительных интересных моментов:

  • .NET управление памятью (сборщик мусора), очевидно, здесь не является проблемой, поскольку NMath все еще зависит от него

  • Преимущество производительности фактически обеспечивается Intel MKL, который предлагает реализации, чрезвычайно оптимизированные для многих процессоров. С моей точки зрения, это ключевой момент. Использование простого и понятного кода C / C ++ не обязательно даст вам превосходную производительность по сравнению с C # /. NET, иногда это даже хуже. Однако C ++ / CLI позволяет использовать все «грязные» опции оптимизации.

5 голосов
/ 21 декабря 2009

Я разместил в блоге статью , посвященную этому вопросу.

3 голосов
/ 02 декабря 2009

Ключ C ++ / CLI . Он позволяет вам компилировать код C ++ в управляемую сборку .NET.

2 голосов
/ 05 ноября 2011

Сегодня это отраслевой стандарт для создания смешанных .Net / нативных библиотек, чтобы использовать преимущества обеих платформ для оптимизации производительности. Не только NMath, многие коммерческие и бесплатные библиотеки с интерфейсом .net работают так. Например: Math.NET Numerics, dnAnalytics , Extreme Optimization, FinMath и многие другие. Интеграция с MKL чрезвычайно популярна для числовых библиотек .net, и большинство из них просто используют сборку Managed C ++ в качестве промежуточного уровня. Но у этого решения есть ряд недостатков:

  1. Intel MKL является проприетарным программным обеспечением, и оно немного дороже. Но некоторые библиотеки, такие как dnAnalytics, предоставляют бесплатную замену функциональности MKL чистым кодом .net. Конечно, это намного медленнее, но это бесплатно и полностью функционально.

  2. Это снижает вашу совместимость, вам нужно иметь мощные управляемые библиотеки ядра C ++ как для 32-битного, так и для 64-битного режима.

  3. Для управления вызовами на уровне сети необходимо выполнить маршалинг, который замедляет выполнение быстрых часто вызываемых операций, таких как Gamma или NormalCDF.

Последние две проблемы решены в библиотеке RTMath FinMath. Я действительно не знаю, как они это сделали, но они предоставляют один чистый .net DLL, который скомпилирован для любой платформы CPU и поддерживает 32-битные и 64-битные. Также я не видел никакого снижения производительности по сравнению с MKL, когда мне нужно вызывать NormalCDF миллиарды раз.

1 голос
/ 02 декабря 2009

Так как (родной) Intel MKL выполняет математику, вы фактически не делаете математику в управляемом коде. Вы просто используете диспетчер памяти из .Net, поэтому результаты легко используются .Net кодом.

0 голосов
/ 08 декабря 2015

Я узнал больше из комментария @ Darin Димитрова к его ответу и комментария @Trevor Misfeldt к комментарию @ Darin. Следовательно, размещение его в качестве ответа для будущих читателей.

NMath использует P / Invoke или C ++ / CLI для вызова собственных функций библиотеки Intel Math Kernel Library, в которых выполняются самые интенсивные вычисления и поэтому они выполняются так быстро.

Время тратится в методах разложения внутри MKL от Intel. Копирование данных также не требуется . Таким образом, не имеет значения, является ли CLI быстрым или нет. Речь идет о том, где происходит исполнение .

Также @ блог Пола также хорошо читается. Вот краткое изложение.

C # - быстро, выделение памяти - нет. Повторно использовать переменные в качестве параметров ref или out вместо возврата новых переменных из методов. Выделение новой переменной потребляет память и замедляет выполнение. @ Хаймо Кучбах объяснил это хорошо.

Если точность не требуется, выигрыш в производительности при переключении с двойной на одинарную точность является значительным (не говоря уже об экономии памяти для хранения данных).

Для многих коротких вычислений вызывать подпрограмму C ++ / cli из C #, прикреплять все указатели к данным, расположенным в управляемом пространстве, а затем вызывать библиотеку Intel, как правило, лучше, чем использовать P / Invoke для вызова библиотеки напрямую из C # , из-за стоимости маршалинга данных. Как уже упоминалось @Haymo Kutschbach в комментариях, для blittable типов, однако, нет никакой разницы между C ++ / CLI и C #. Массивы blittable типов и классов, которые содержат только blittable члены, прикрепляются, а не копируются во время маршалинга. См. https://msdn.microsoft.com/en-us/library/75dwhxf7(v=vs.110).aspx для получения списка blittable и non-blittable типов.

...