Скорость .NET в числовых вычислениях
-
11-09-2019 - |
Вопрос
По моему опыту, .NET в 2–3 раза медленнее, чем собственный код.(Я применил L-BFGS для многомерной оптимизации).
Я проследил рекламу в stackoverflow доhttp://www.centerspace.net/products/
скорость действительно потрясающая, скорость близка к нативному коду.Как они могут это сделать?Они сказали, что:
К.Является ли NMath «чистым» .NET?
А.Ответ в некоторой степени зависит от вашего определения «чистого .NET».NMath написан на C# плюс небольшой уровень Managed C++.Однако для повышения производительности базовых операций линейной алгебры NMath использует собственную библиотеку ядра Intel Math (входит в состав NMath).Но здесь нет ни COM-компонентов, ни DLL — только сборки .NET.Кроме того, вся память, выделенная на уровне управляемого C++ и используемая собственным кодом, выделяется из управляемой кучи.
Может ли кто-нибудь объяснить мне больше?
Решение
Вопрос о C++/CLI верен.Для полноты картины еще два интересных момента:
Управление памятью .NET (сборщик мусора), очевидно, не является проблемой здесь, поскольку NMath все еще зависит от него.
Преимущество в производительности фактически обеспечивается Intel MKL, которая предлагает реализации, чрезвычайно оптимизированные для многих процессоров.С моей точки зрения, это решающий момент.Использование простого и наивного кода C/C++ не обязательно обеспечит более высокую производительность по сравнению с C#/.NET, а иногда даже хуже.Однако C++/CLI позволяет использовать все «грязные» варианты оптимизации.
Другие советы
Как они могут это сделать?
Как и большинство числовых библиотек для .NET, NMath представляет собой не более чем оболочку Intel MKL, встроенного в сборку .NET, вероятно, путем связывания с C++/CLI для создания смешанная сборка.Вероятно, вы только что протестировали те фрагменты, которые на самом деле не написаны в .NET.
Статьи журнала F#.NET Числовые библиотеки:специальные функции, интерполяция и случайные числа (16 марта 2008 г.) и Числовые библиотеки:линейная алгебра и спектральные методы (16 апреля 2008 г.) протестировал немало функциональных возможностей, и NMath оказалась самой медленной из всех коммерческих библиотек.Их PRNG был медленнее всех остальных и на 50 % медленнее, чем бесплатная библиотека Math.NET, отсутствовали некоторые базовые функции (например,умение рассчитывать Gamma(-0.5)
) и другие базовые функции (функции, связанные с Gamma), которые они предоставляли, были сломаны.И Extreme Optimization, и Bluebit превзошли NMath в тесте собственного решателя.В то время NMath даже не предоставлял преобразования Фурье.
Еще более удивительно то, что расхождения в производительности иногда были огромными.Самая дорогая протестированная нами коммерческая числовая библиотека (IMSL) была более чем в 500 раз медленнее, чем бесплатная библиотека FFTW в тесте FFT и никто библиотек в то время использовали несколько ядер.
Фактически, именно низкое качество этих библиотек побудило нас коммерциализировать наши собственные F# для числовых значений библиотеку (которая представляет собой 100% чистый код F#).
Я один из ведущих разработчиков ILЦифровые значения.Так что я, очевидно, предвзят ;) Но мы более раскрыты в отношении наших внутренних качеств, поэтому я дам некоторое представление о наш «секреты» скорости.
Все зависит от того, как используются системные ресурсы!Если вам нужна чистая скорость и вам нужно обрабатывать большие массивы, вы обязательно (в порядке важности, самое важное сначала)
Управляйте своей памятью правильно!«Наивное» управление памятью приведет к снижению производительности, поскольку оно сильно нагружает сборщик мусора, вызывает фрагментацию памяти и ухудшает локальность памяти (следовательно, производительность кэша).В среде сбора мусора, такой как .NET, это сводится к предотвращению частого выделения памяти.Для достижения этой цели в ILNumerics мы реализовали высокопроизводительный пул памяти (и детерминированное удаление временных массивов для получения красивого и удобного синтаксиса без неуклюжей семантики функций).
Используйте параллелизм!Это нацелено на оба:параллелизм на уровне потоков и параллелизм на уровне данных.Несколько ядер используются для потоковой обработки частей вычислений, требующих большого объема вычислений.На процессорах X86/X64 расширения SIMD/мультимедиа, такие как SSE.XX и AVX, позволяют выполнять небольшую, но эффективную векторизацию.Они не доступны напрямую в современных языках .NET.И это единственная причина, по которой MKL может работать быстрее, чем «чистый» код .NET.(Но решения уже появляются.)
Для архивирования скорость высокооптимизированных языков как и в 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 и опубликовать результаты сравнения, и я уверен, что другие библиотеки, перечисленные здесь, захотят последовать моему примеру.(?)
Я разместил блог статья обращаясь к этому вопросу.
Ключ С++/CLI.Он позволяет компилировать код C++ в управляемую сборку .NET.
Сегодня отраслевым стандартом является создание смешанных .Net/родных библиотек, чтобы использовать преимущества обеих платформ для оптимизации производительности.Не только NMath, многие коммерческие и бесплатные библиотеки с интерфейсом .net работают так.Например:числовые значения Math.NET, днАналитика, Экстремальная оптимизация, ФинМатематика и многие другие.Интеграция с MKL чрезвычайно популярна для числовых библиотек .net, и большинство из них просто используют управляемую сборку C++ в качестве промежуточного уровня.Но такое решение имеет ряд недостатков:
Intel MKL — это проприетарное программное обеспечение, и оно немного дороже.Но некоторые библиотеки, такие как dnAnalytics, предоставляют бесплатную замену функциональности MKL чистым кодом .net.Конечно, это намного медленнее, но зато бесплатно и полностью функционально.
Это снижает вашу совместимость: вам нужны тяжелые управляемые библиотеки DLL ядра C++ как для 32-битного, так и для 64-битного режима.
Вызовы, управляемые как собственные, требуют выполнения маршалинга, который замедляет производительность быстрых часто вызываемых операций, таких как Gamma или NormalCDF.
Последние две задачи решены в библиотеке RTMath FinMath.Я действительно не знаю, как они это сделали, но они предоставляют одну чистую .net dll, которая скомпилирована для любой платформы ЦП и поддерживает 32-битные и 64-битные версии.Также я не заметил никакого снижения производительности по сравнению с MKL, когда мне нужно вызвать NormalCDF миллиарды раз.
Поскольку (родной) Intel MKL выполняет математические операции, вы фактически не выполняете математические операции в управляемом коде.Вы просто используете диспетчер памяти из .Net, поэтому результаты легко используются кодом .Net.
Я узнал больше из комментария @Darin Dimitrov к его ответу и комментария @Trevor Misfeldt к комментарию @Darin.Поэтому публикую это как ответ для будущих читателей.
NMath использует P/Invoke или C++/CLI для вызова собственных функций библиотеки ядра Intel Math, где выполняются наиболее интенсивные вычисления и поэтому это происходит так быстро.
А время потрачено в методы декомпозиции внутри Intel MKL. Копирование данных не требуется., или.Так, дело не в том, быстрый ли CLI или нет. Речь идет о том, где происходит казнь.
Также полезно читать блог @Paul.Вот резюме.
C# — быстрый, а распределение памяти — нет. Повторно используйте переменные в качестве параметров ref или out., вместо возврата новых переменных из методов.Выделение новой переменной потребляет память и замедляет выполнение.@Haymo Kutschbach хорошо это объяснил.
Если точность не обязательна, то прирост производительности при переходе от двойной точности к одинарной будет значительным (не говоря уже об экономии памяти для хранения данных).
Для многих коротких вычислений вызвать подпрограмму C++/cli из C#, закрепить все указатели на данные, выделенные в управляемом пространстве, а затем вызвать библиотеку Intel, как правило, лучше, чем использовать P/Invoke для вызова библиотеки непосредственно из C#, поскольку стоимость маршалирования данных.Как упоминал @Haymo Kutschbach в комментариях, для преобразуемых типов нет разницы между C++/CLI и C#.Массивы преобразуемых типов и классов, которые содержат только преобразуемые элементы, во время маршалинга закрепляются, а не копируются.Ссылаться https://msdn.microsoft.com/en-us/library/75dwhxf7(v=vs.110).aspx для списка преобразуемых и непреобразуемых типов.