私はILNumericsの主任開発者の 1 人です。だから私は明らかに偏っています;)しかし、私たちは内部に関してより開示されているので、スピードの「秘密」についていくつかの洞察を与えます.
すべては、システム リソースがどのように利用されるかにかかっています。純粋な速度が必要で、大規模な配列を処理する必要がある場合は、次のことを確認してください (重要度順、最も重要なものが最初)。
適切にメモリを管理してください!「素朴な」メモリ管理はパフォーマンスの低下につながります。これは、GC に過度のストレスを与え、メモリの断片化を引き起こし、メモリの局所性を低下させるためです (したがって、キャッシュ パフォーマンス)。.NET のようなガベージ コレクション環境では、これは要するに、頻繁なメモリ割り当てを防ぐことになります。ILNumerics では、この目標を達成するために高パフォーマンスのメモリ プールを実装しました (そして、不器用な関数セマンティクスのない適切で快適な構文を得るために一時配列を決定論的に破棄します)。
並列処理を活用!これは、スレッド レベルの並列処理とデータ レベルの並列処理の両方を対象としています。複数のコアは、計算の計算集約的な部分をスレッド化することによって利用されます。X86/X64 CPU では、SSE.XX や AVX などの SIMD/マルチメディア拡張により、小さいながらも効果的なベクトル化が可能になります。それらは、現在の .NET 言語では直接アドレス指定できません。これが、MKL が「純粋な」.NET コードよりも依然として高速である唯一の理由です。(しかし、ソリューションはすでに上昇しています。)
FORTRAN や C++ などの高度に最適化された言語の速度を実現するには、コードに対して行ったのと同じ最適化をコードに適用する必要があります。C# はオプション do do so を提供します。
これらの予防措置は、この順序で実行する必要があることに注意してください。ボトルネックがメモリ帯域幅であり、プロセッサがほとんどの時間を新しいデータの待機に費やしている場合、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 に変換し、比較結果を投稿します。ここにリストされている他のライブラリも従いたいと思います。(?)