質問

私の経験では、.NET はネイティブ コードより 2 ~ 3 倍遅いです。(多変量最適化のために L-BFGS を実装しました)。

stackoverflow 上の広告を追跡したところ、http://www.centerspace.net/products/

速度は本当に驚くべきもので、ネイティブコードに近い速度です。彼らはどうやってそんなことができるのでしょうか?彼らはこう言いました:

Q.NMath は「純粋な」 .NET ですか?

A.答えは、「純粋な .NET」の定義によって多少異なります。NMath は C# で書かれており、さらに小さなマネージド C++ レイヤーが追加されています。ただし、基本的な線形代数演算のパフォーマンスを向上させるために、NMath はネイティブのインテル マス カーネル ライブラリ (NMath に含まれる) に依存しています。ただし、COM コンポーネントや DLL はなく、.NET アセンブリがあるだけです。また、マネージド C++ レイヤーに割り当てられ、ネイティブ コードによって使用されるすべてのメモリは、マネージド ヒープから割り当てられます。

誰かもっと詳しく説明してもらえますか?

役に立ちましたか?

解決

C++/CLI に関する指摘は正しいです。全体像を完成させるには、興味深い点を 2 つ追加するだけです。

  • NMath は依然としてそれに依存しているため、.NET メモリ管理 (ガベージ コレクター) は明らかにここでは問題ではありません。

  • 実際、パフォーマンス上の利点は、多くの CPU に対して非常に最適化された実装を提供するインテル MKL によってもたらされます。私の観点からすると、これが重要なポイントです。単純で単純な C/C++ コードを使用しても、必ずしも C#/.NET よりも優れたパフォーマンスが得られるとは限りません。場合によっては、さらに悪くなる場合もあります。ただし、C++/CLI では、すべての「ダーティ」最適化オプションを利用できます。

他のヒント

彼らはどうやってそんなことができるのでしょうか?

.NET のほとんどの数値ライブラリと同様、NMath は、.NET アセンブリに埋め込まれたインテル MKL のラッパーにすぎません。おそらく、C++/CLI とリンクして、 混合アセンブリ. 。おそらく、実際には .NET で書かれていない部分のベンチマークを行ったことがあるでしょう。

F#.NET ジャーナルの記事 数値ライブラリ:特殊関数、補間、乱数 (2008 年 3 月 16 日) および 数値ライブラリ:線形代数とスペクトル法 (2008 年 4 月 16 日) はかなりの機能をテストしましたが、NMath は実際にすべての商用ライブラリの中で最も遅かったです。彼らの PRNG は他の PRNG よりも遅く、無料の Math.NET ライブラリよりも 50% 遅く、いくつかの基本的な機能が欠けていました (例:計算する能力 Gamma(-0.5)) およびその他の基本機能 (提供されていたガンマ関連の機能) が壊れていました。Extreme Optimization と Bluebit はいずれも、eigensolver ベンチマークで NMath を上回りました。当時、NMath はフーリエ変換さえ提供していませんでした。

さらに驚くべきことに、パフォーマンスの差異が大きくなる場合もありました。私たちがテストした最も高価な商用数値ライブラリ (IMSL) は、FFT ベンチマークでは無料の FFTW ライブラリよりも 500 倍以上遅かったです。 なし 当時、そのうちのライブラリは複数のコアを使用していました。

実際、これらのライブラリの品質が低かったからこそ、私たちは独自のライブラリを商品化することを奨励しました。 数値の F# ライブラリ (100% 純粋な F# コード)。

私は主任開発者の一人です IL数値. 。したがって、明らかに私は偏見を持っています ;) しかし、私たちは内部についてはより開示されているので、いくつかの洞察を提供します 私たちの スピードの「秘密」。

すべてはシステム リソースがどのように利用されるかによって決まります。純粋に速度を重視し、大規模な配列を処理する必要がある場合は、必ず次のことを実行します (重要度の順、最も重要なものが最初)

  1. 記憶を適切に管理しましょう。「単純な」メモリ管理は、GC に大きなストレスを与え、メモリの断片化を引き起こし、メモリの局所性 (したがってキャッシュのパフォーマンス) を低下させるため、パフォーマンスの低下につながります。.NET のようなガベージ コレクション環境では、これはつまるところ、頻繁なメモリ割り当てを防ぐことになります。ILNumerics では、この目標を達成するために、高性能メモリ プールを実装しました (そして、ぎこちない関数セマンティクスのない快適な構文を取得するための一時配列の決定論的な破棄)。

  2. 並列処理を活用しましょう!これは次の両方を対象としています。スレッドレベルの並列処理とデータレベルの並列処理。複数のコアは、計算の集中的な部分をスレッド化することによって利用されます。X86/X64 CPU では、SSE.XX や AVX などの SIMD/マルチメディア拡張機能により、小さいながらも効果的なベクトル化が可能になります。これらは、現在の .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 に変換し、比較結果を投稿します。ここにリストされている他のライブラリもきっとこれに従うでしょう。(?)

この質問に対処する

私はブログの記事ます。

キーは C ++ / CLI のです。それはあなたが管理し、.NETアセンブリにC ++コードをコンパイルすることができます。

現在、両方のプラットフォームの利点を活用してパフォーマンスを最適化するために、.Net/ネイティブ ライブラリを混合して作成することが業界標準になっています。NMath だけでなく、.net インターフェイスを備えた多くの商用ライブラリや無料ライブラリもこのように動作します。例えば:Math.NET 数値、 dnAnalytics, 、極限の最適化、 フィンマス 他にもたくさんあります。MKL との統合は .net 数値​​ライブラリで非常に人気があり、そのほとんどは中間レベルとしてマネージ C++ アセンブリを使用するだけです。しかし、この解決策には多くの欠点があります。

  1. Intel MKL は独自のソフトウェアであり、少し高価です。ただし、dnAnalytics などの一部のライブラリでは、MKL 機能を純粋な .net コードで無料で置き換えることができます。もちろん、速度はかなり遅くなりますが、無料で完全に機能します。

  2. これにより、互換性が低下し、32 ビット モードと 64 ビット モードの両方でヘビー マネージド C++ カーネル DLL が必要になります。

  3. マネージド呼び出しからネイティブ呼び出しへのマーシャリングを実行する必要があるため、Gamma や NormalCDF など、頻繁に呼び出される高速操作のパフォーマンスが低下します。

最後の 2 つの問題は RTMath FinMath ライブラリで解決されました。彼らがどうやってそれを行ったのかはよくわかりませんが、あらゆる CPU プラットフォーム用にコンパイルされ、32 ビットと 64 ビットをサポートする単一の純粋な .net dll を提供します。また、NormalCDF を何十億回も呼び出す必要がある場合でも、MKL に対するパフォーマンスの低下は見られませんでした。

は、(ネイティブ)インテルMKLは、数学をやっているので、あなたが実際にマネージコードで数学をやっていません。成果を簡単に.NETコードで使用されているので、あなたは単に、.NETからメモリマネージャを使用しています。

彼の回答に対する@Darin Dimitrovのコメントと、@Darinのコメントに対する@Trevor Misfeldtのコメントからさらに多くのことを学びました。したがって、将来の読者のために、回答として投稿します。

NMath は、P/Invoke または C++/CLI を使用してインテル マス カーネル ライブラリのネイティブ関数を呼び出します。ここで最も集中的な計算が行われ、それが高速な理由です。

時間が費やされるIntel の MKL 内の分解メソッド. データのコピーは必要ありません, 、 どちらか。それで、 CLIが速いかどうかは問題ではありません か否か。 処刑が行われる場所についてです.

@Paul のブログもおすすめです。概要は次のとおりです。

C# は高速ですが、メモリ割り当ては高速ではありません。 変数を ref または out パラメータとして再利用します, メソッドから新しい変数を返す代わりに。新しい変数を割り当てるとメモリが消費され、実行が遅くなります。@Haymo Kutschbach がこれについてよく説明しています。

精度が必要ない場合、倍精度から単精度に切り替えることでパフォーマンスが大幅に向上します (データ ストレージのメモリ節約は言うまでもありません)。

多くの短い計算の場合、C# から C++/cli ルーチンを呼び出し、すべてのポインターをマネージド領域に割り当てられたデータに固定してから 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 タイプと非 blittable タイプのリストについては、

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top