Я один из ведущих разработчиков ILNumerics . Так что я предвзят, очевидно;) Но мы более раскрыты в отношении наших внутренних функций, поэтому я расскажу о наших скоростных «секретах».
Все зависит от того, как используются системные ресурсы! Если вы работаете на чистой скорости и вам нужно работать с большими массивами, вы обязательно (упорядочены по важности, в первую очередь по важности)
Управляйте своей памятью соответствующим образом! «Наивное» управление памятью приведет к снижению производительности, так как оно сильно влияет на сборщик мусора, вызывает фрагментацию памяти и ухудшает локальность памяти (следовательно, производительность кэша). В среде с сборкой мусора, такой как .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 и опубликовать результаты сравнения, и я уверен, что другие библиотеки, перечисленные здесь, хотели бы следовать. (?)