私はしばらくこの問題について疑問に思っていました。
もちろん、C#には速度が最適化されていないものがあります。そのため、これらのオブジェクトまたは言語調整(LinQなど)を使用すると、コードが遅くなる可能性があります。
ただし、これらの調整を使用せずに、C#とC++で同じコードを比較するだけの場合(簡単に変換できます)。本当にそんなに遅いのでしょうか?
理論的にはJITコンパイラーがコードをリアルタイムで最適化し、より良い結果を得るため、C#がさらに高速になる場合があることを示す比較を見てきました。
JITコンパイラーはリアルタイムでコードをコンパイルしますが、それは1回のオーバーヘッドであり、同じコード(一度到達してコンパイルされたもの)を実行時に再度コンパイルする必要はありません。
GCは、何千ものオブジェクトを作成および破棄しない限り(StringBuilderの代わりにStringを使用するなど)、多くのオーバーヘッドも追加しません。また、C++でそれを行うこともコストがかかります。
私が提起したいもう1つのポイントは、.Netで導入されたDLL間のより良い通信です。 .Netプラットフォームは、マネージCOMベースのDLLよりもはるかに良好に通信します。
言語が遅くなる固有の理由は見当たりませんし、C#がC++より遅いとは本当に思いません(経験と良い説明の両方から)。
したがって、C#で記述された同じコードの一部は、C++の同じコードよりも遅くなりますか?
もしそうなら、なぜ?
その他の参考資料(これについて少し説明しますが、理由については説明しません):
警告:あなたが尋ねた質問は本当に複雑で、おそらくあなたが思っているよりもずっと複雑です。その結果、これは本当に長い答えになります。
純粋に理論的な観点からすると、おそらくこれに対する簡単な答えがあります。C#について、C++ほど高速であることを本当に妨げるものは(おそらく)何もありません。ただし、理論にもかかわらず、状況によっては、isが遅いという実際的な理由がいくつかあります。
言語の機能、仮想マシンの実行、ガベージコレクションの3つの基本的な違いを検討します。後者の2つはしばしば一緒になりますが、独立している可能性があるため、別々に見ていきます。
C++はテンプレートとテンプレートシステムの機能を非常に重視しており、主にコンパイル時に可能な限り実行できるようにすることを目的としているため、プログラムの観点からは「静的」です。テンプレートのメタプログラミングにより、コンパイル時に完全に任意の計算を実行できます(つまり、テンプレートシステムはチューリング完了です)。そのため、基本的にユーザーからの入力に依存しないものはすべてコンパイル時に計算できるため、実行時は単純に定数になります。ただし、これへの入力には型情報などを含めることができるため、C#の実行時のリフレクションを介して行うことの多くは通常、C++のテンプレートメタプログラミングを介してコンパイル時に行われます。ただし、実行速度と汎用性の間には必ずトレードオフがあります。テンプレートでできること、静的に行うことはできますが、リフレクションでできることをすべて行うことはできません。
言語機能の違いは、単にいくつかのC#をC++に音訳する(またはその逆)だけで2つの言語を比較しようとすると、ほとんど意味のない結果と誤解を招く結果が生じる可能性が高いことを意味します(他のほとんどの言語のペアについても同様です)同様に)。簡単な事実は、数行のコードなどよりも大きいものについては、そのような比較がそれらの言語の方法について何かを教えてくれるのと同じ方法(または同じ方法に十分近い)を使用する可能性はほとんどありません実生活で働きます。
ほぼすべての合理的な最新のVMと同様に、Microsoft for .NETはJIT(別名「動的」)コンパイルを実行できます。ただし、これにはいくつかのトレードオフがあります。
主に、コードの最適化(他のほとんどの最適化問題と同様)は、主にNP完全な問題です。本当に些細な/おもちゃのプログラム以外については、結果を本当に「最適化」しない(つまり、真の最適化を見つけられない)ことはほぼ保証されています-オプティマイザーは単にコードを作成しますbetter以前よりも。ただし、よく知られているかなりの数の最適化の実行には、かなりの時間がかかります(多くの場合、メモリ)。 JITコンパイラーでは、ユーザーはコンパイラーの実行を待機しています。より高価な最適化手法のほとんどは除外されています。静的コンパイルには2つの利点があります。まず、遅い場合(大規模システムの構築など)、通常はサーバー上で実行され、nobodyが費やされますそれを待っている時間。第二に、実行可能ファイルを生成一度、多くの人が何度も使用できます。 1つ目は、最適化のコストを最小化します。 2番目の方法では、はるかに多くの実行に対してはるかに小さいコストが償却されます。
元の質問(および他の多くのWebサイト)で述べたように、JITコンパイルはターゲット環境をよりよく認識できる可能性があり、(少なくとも理論的には)この利点を相殺するはずです。この要素が静的コンパイルの不利な点の少なくとも一部を相殺できることは間違いありません。いくつかのかなり特定のタイプのコードおよびターゲット環境では、静的コンパイルの利点よりも(場合によってはかなり劇的に)canを上回ることもあります。少なくとも私のテストと経験では、これはかなり珍しいことです。ターゲット依存の最適化は、ほとんどの場合、かなり小さな違いを生じるか、かなり特定のタイプの問題にのみ(とにかく自動的に)適用できるようです。これが明らかになるのは、比較的古いプログラムを最新のマシンで実行している場合です。 C++で書かれた古いプログラムは、おそらく32ビットコードにコンパイルされていて、最新の64ビットプロセッサ上でも32ビットコードを使用し続けるでしょう。 C#で記述されたプログラムはバイトコードにコンパイルされ、VMは64ビットマシンコードにコンパイルされます。このプログラムが64ビットコードとして実行することで大きなメリットが得られる場合、 64ビットプロセッサがかなり新しい頃、これはかなりの量で発生しました。64ビットプロセッサの恩恵を受ける可能性のある最近のコードは、通常、静的に64ビットコードにコンパイルされますが、 。
VMを使用すると、キャッシュの使用率が向上する可能性があります。VMの命令は、多くの場合、ネイティブのマシン命令よりもコンパクトです。これにより、必要なときに特定のコードがキャッシュ内に存在する可能性が高くなります。これにより、VM人々は最初に期待します-lotを実行することができますone キャッシュミス。
また、この要素は、2つの間で必ずしも違いがないことはありません。 (たとえば)C++コンパイラが仮想マシン上で実行する出力を生成するのを妨げるものは何もありません(JITの有無にかかわらず)。実際、MicrosoftのC++/CLIはnearlyです-(ほぼ)準拠するC++コンパイラ(ただし、多くの拡張機能を備えています)は、意図した出力を生成します仮想マシンで実行します。
その逆も同様です。Microsoftには.NET Nativeがあり、C#(またはVB.NET)コードをネイティブ実行可能ファイルにコンパイルします。これにより、一般にC++にはるかに近いパフォーマンスが得られますが、C#/ VBの機能は保持されます(たとえば、ネイティブコードにコンパイルされたC#は、リフレクションを引き続きサポートします)。パフォーマンスが集中するC#コードがある場合、これが役立つ場合があります。
私が見てきたことから、ガベージコレクションはこれらの3つの要因の中で最も貧しい人々が理解していると思います。分かりやすい例として、ここでの質問は、「何千ものオブジェクトを作成して破棄しない限り、GCはオーバーヘッドをあまり追加しません[...]」。実際には、数千のオブジェクトを作成および破壊すると、ガベージコレクションによるオーバーヘッドは通常かなり低くなります。 .NETは、さまざまなコピーコレクターである世代別スカベンジャーを使用します。ガベージコレクターは、ポインター/参照が既知である「場所」(レジスターや実行スタックなど)から開始することで機能します。次に、ヒープに割り当てられたオブジェクトへのポインターを「追跡」します。すべてのチェーンの最後まですべてのオブジェクトをたどり、(少なくとも潜在的に)アクセス可能なすべてのオブジェクトを見つけるまで、それらのオブジェクトをさらにポインタ/参照について調べます。次のステップでは、使用中の(または少なくともmight be)であるすべてのオブジェクトを取得し、それらすべてをコピーしてヒープを圧縮しますヒープで管理されているメモリの一端にある連続したチャンクにその後、残りのメモリは解放されます(モジュロファイナライザは実行する必要がありますが、少なくとも適切に記述されたコードでは、現時点では無視するほどまれです)。
これは、大量のオブジェクトを作成および破棄した場合、ガベージコレクションがオーバーヘッドをほとんど追加しないことを意味します。ガベージコレクションサイクルにかかる時間は、作成されたがnot破壊されたオブジェクトの数にほぼ完全に依存します。急いでオブジェクトを作成および破棄することの主な結果は、GCをより頻繁に実行する必要があるということですが、各サイクルは依然として高速です。オブジェクトを作成してdo n't破棄しない場合、GCはより頻繁に実行されますand潜在的に生存しているオブジェクトへのポインターの追跡に時間がかかるため、各サイクルは大幅に遅くなります。andまだ使用中です。
これに対抗するために、世代の清掃は、haveがしばらく「生きている」オブジェクトがかなり長く生き続ける可能性が高いという仮定に基づいて動作しますより長いです。これに基づいて、いくつかのガベージコレクションサイクルを生き残ったオブジェクトが「終身」になるシステムがあり、ガベージコレクターは、それらがまだ使用中であると単純に仮定し始めるため、すべてのサイクルでコピーするのではなく、単に去りますそれらだけです。これは、多くの場合、世代別の清掃が他のほとんどの形式のGCよりもかなり低いオーバーヘッドであるために十分な妥当な仮定です。
多くの場合、「手動」メモリ管理はあまり理解されていません。ほんの一例として、比較の多くの試みは、すべての手動メモリ管理が1つの特定のモデル(たとえば、最適な割り当て)にも従うことを前提としています。多くの場合、これは、ガベージコレクションに関する多くの人々の信念(たとえば、参照カウントを使用して通常行われているという一般的な仮定)よりも現実に近い場合がほとんどありません。
ガベージコレクションとの手動メモリ管理の両方のさまざまな戦略を考えると、全体的な速度の観点から2つを比較することは非常に困難です。メモリーの割り当てと解放の速度を(単独で)比較しようとすると、ほとんど無意味で、最悪の場合は完全に誤解を招く結果を生成することがほぼ保証されます。
かなりの数のブログ、ウェブサイト、雑誌記事などが、「客観的な」証拠を何らかの方向で提供していると主張しているので、私もそのテーマに2セントの価値を入れます。
これらのベンチマークのほとんどは、車をレースすることを決定するティーンエイジャーのようなもので、勝った人は両方の車をキープします。ただし、Webサイトは1つの重要な点で異なります。ベンチマークを公開しているユーザーは、両方の車を運転することができます。奇妙な偶然で、彼の車は常に勝ちます、そして、他の誰もが「私を信頼して、私は本当にあなたの車をできるだけ速く運転していた」行きます。」
何もほとんど意味のない結果を生み出す貧弱なベンチマークを書くのは簡単です。意味のあるものを作成するベンチマークを設計するのに必要なスキルに近い場所にいる人なら、だれでも、自分が望むと決めた結果を出すベンチマークを作成するスキルを持っています。実際、意味のある結果を実際に生成するコードよりも特定の結果を生成するコードを記述する方がおそらくeasierです。
友人のジェームズ・カンゼが言ったように、「自分を偽造しなかったベンチマークを決して信用しないでください」。
簡単な答えはありません。コインをひっくり返して勝者を選択し、勝つ割合として(たとえば)1から20の間の数字を選んで、合理的で公正なベンチマークのように見えるコードを書くことができると確信しています。 (少なくとも一部のターゲットプロセッサでは、異なるプロセッサがパーセンテージを少し変更する可能性があります)という、先例のない結論を出しました。
他の人が指摘したように、mostコードの場合、速度はほとんど無関係です。その結果(無視されることが多い)は、速度が重要な小さなコードでは、通常はlotが重要です。少なくとも私の経験では、本当に重要なコードに関しては、C++がほとんど常に勝者です。 C#を好む要因は間違いなくありますが、実際には、C++を好む要因よりも重要であるようです。確かに、選択した結果を示すベンチマークを見つけることができますが、実際のコードを記述するときは、C#よりもC++の方がほとんど常に高速にできます。書くのにもっとスキルや努力が必要な場合もあればそうでない場合もありますが、事実上常に可能です。
常に「最速」言語を使用する必要があるわけではないので(そして私はこれを緩く使用します)?速いからといって、フェラーリで仕事をするつもりはありません...
2005年頃、ネイティブ/マネージドフェンスの両側から2人のMSパフォーマンスエキスパートが同じ質問に答えようとしました。彼らの方法とプロセスは依然として魅力的であり、結論は今日でも維持されています-そして、私は情報に基づいた答えを出すためのこれ以上の試みを知りません。彼らは、パフォーマンスの違いに対する潜在的な理由の議論は仮説的で無駄であり、実際の議論にはそのような違いの現実世界への影響について経験的根拠が必要であると述べた。
したがって、 Old New Raymond Chen および Rico Mariani は、友好的な競争のルールを設定します。中国語/英語の辞書が玩具アプリケーションのコンテキストとして選択されました。趣味のサイドプロジェクトとしてコーディングするのに十分シンプルでありながら、些細でないデータ使用パターンを示すのに十分複雑です。ルールはシンプルに始まりました-レイモンドは簡単なC++実装をコーディングし、リコはそれをC#line by lineに移行しました。洗練されたものはなく、両方の実装がベンチマークを実行しました。その後、最適化のいくつかの反復が行われました。
詳細はこちら: 1 、 2 、、 4 、 5 、 6 、 7 、 8 、 9 、 1 、 11 、 12 、 1 、 14 、 15 、 16 。
このタイタンズの対話は非常に教育的であり、私は心から飛び込むことを心からお勧めします-しかし、時間や忍耐が足りない場合は、ジェフ・アトウッド ボトムラインを美しくまとめました :
最終的に、C++は2倍速くなりましたが、最初は13倍遅くなりました。
リコとして 要約 :
だから私は私の敗北に恥じていますか?ほとんどない。マネージコードは、ほとんど労力をかけずに非常に良い結果を達成しました。管理バージョンを無効にするには、レイモンドは次のことをしなければなりませんでした。
独自のファイル/ ioのものを書く
独自の文字列クラスを書く
独自のアロケーターを作成する
彼自身の国際的なマッピングを書く
もちろん、利用可能な低レベルのライブラリを使用してこれを行いましたが、それでも多くの作業が必要です。 STLプログラムに残っているものを呼び出すことができますか?そうは思いません。
それはまだ11年の私の経験であり、C#/ C++バージョンの数を後で知っている人です。
もちろん、これらの2つの言語が大きく異なる設計目標を達成するのは偶然ではありません。 C#は、開発コストが主な考慮事項(まだソフトウェアの大部分)で使用されることを望んでおり、C++は、マシンからパフォーマンスの最後の1オンスを絞り出すために費用を節約しない場所に輝いています:ゲーム、アルゴリズム取引、データセンターなど.
C++には、パフォーマンスのために常にエッジがあります。 C#では、メモリを処理することができず、文字通り、仕事をするために利用できるリソースが大量にあります。
自分に質問する必要があるのは、どれがあなたの時間を節約するかということです。マシンは現在非常に強力であり、コードのほとんどは、最短時間で最大限の価値を得ることができる言語で実行する必要があります。
C#で時間がかかりすぎるコア処理がある場合は、C++を構築し、C#で相互運用できます。
コードのパフォーマンスについて考えるのをやめます。価値の構築を開始します。
それを見るより良い方法は、スティックと泥のパラダイムに従うのではなく抽象化するため、C/C++よりも遅くなります。それは理由のためにシステムプログラミングと呼ばれます、あなたは木目または裸の金属に対してプログラムします。これにより、C#やJavaなどの他の言語では達成できない速度も実現します。しかし、悲しいかなCの根源はすべて物事をハードな方法で行うことであるため、ほとんどの場合、より多くのコードを記述し、デバッグにより多くの時間を費やします。
Cは大文字と小文字を区別し、C++のオブジェクトも厳密なルールセットに従います。たとえば、紫色のアイスクリームコーンは青いアイスクリームコーンと同じではない場合がありますが、コーンである可能性がありますが、必ずしもコーンファミリーに属しているとは限らず、どのコーンをバグアウトするかを定義し忘れている場合もあります。したがって、アイスクリームの特性はクローンである場合とそうでない場合があります。現在、速度の引数であるC/C++は、スタックとヒープのアプローチを使用しています。これは、ベアメタルがメタルを取得する場所です。
ブーストライブラリを使用すると、残念ながら、ほとんどのゲームスタジオが標準ライブラリに固執する驚異的な速度を実現できます。このもう1つの理由は、C/C++で書かれたソフトウェアは、単一のファイルではなくファイルの巨大なコレクションであるため、ファイルサイズが膨大になる傾向があるためかもしれません。また、すべてのオペレーティングシステムがCで記述されていることに注意してください。一般に、なぜ高速化できるのかを質問する必要があるのはなぜですか。
また、キャッシュは純粋なメモリ管理よりも高速ではありません。申し訳ありませんが、これはファンアウトしません。メモリは物理的なものであり、キャッシングはソフトウェアがパフォーマンスを向上させるために行うものです。物理メモリキャッシングがなければ、単に存在しないと考えることもできます。自動であろうと手動であろうと、メモリを何らかのレベルで管理する必要があるという事実は無効ではありません。
ところで、タイムクリティカルなアプリケーションは、主にガベージコレクションが実行されるタイミングが不確実であるため、C#またはJavaでコーディングされていません。
現代では、アプリケーションや実行速度は以前ほど重要ではありません。開発スケジュール、正確性、堅牢性が優先事項です。アプリケーションの高速バージョンは、バグが多く、クラッシュがひどく、さらに悪い場合、市場に出回ったり、展開したりする機会を逃した場合、良くありません。
開発スケジュールが優先事項であるため、開発を加速する新しい言語が登場しています。 C#はこれらの1つです。 C#は、一般的な問題の原因となるC++から機能を削除することにより、正確性と堅牢性も支援します。1つの例はポインターです。
C#で開発されたアプリケーションとC++を使用して開発されたアプリケーションの実行速度の違いは、ほとんどのプラットフォームで無視できます。これは、実行のボトルネックは言語に依存せず、通常はオペレーティングシステムまたはI/Oに依存するという事実によるものです。たとえば、C++が関数を5ミリ秒で実行するが、C#が2ミリ秒を使用し、データの待機に2秒かかる場合、関数で費やされる時間はデータの待機時間に比べてわずかです。
開発者、プラットフォーム、プロジェクトに最適な言語を選択してください。正確性、堅牢性、展開の目標に向かって取り組みます。アプリケーションの速度はバグとして扱う必要があります。優先順位を付け、他のバグと比較し、必要に応じて修正します。
より高速なroute(C#)がある場合、C++で最適化の方法であまり必要としない小さなアプリケーションを書くのはなぜですか?
特定のシステムでベンチマークを実行しない限り、質問に対する正確な回答を得ることは実際には不可能です。ただし、C#やC++などのプログラミング言語の根本的な違いについて考えることは依然として興味深いです。
コンパイル
C#コードを実行するには、コードをJITする追加の手順が必要です。パフォーマンスに関しては、C++を優先します。また、JITコンパイラは、JITされたコード単位(メソッドなど)内で生成されたコードのみを最適化できますが、C++コンパイラは、より積極的な手法を使用してメソッド呼び出し全体で最適化できます。
ただし、JITコンパイラは、生成されたマシンコードを最適化して基礎となるハードウェアに厳密に一致させることができるため、追加のハードウェア機能があればそれを利用できます。私の知る限り、.NET JITコンパイラーはそれを行いませんが、Pentium CPUとは異なり、Atomに対して異なるコードを生成できると考えられます。
メモリアクセス
ガベージコレクションアーキテクチャは、多くの場合、標準のC++コードよりも最適なメモリアクセスパターンを作成できます。第1世代に使用されるメモリ領域が十分に小さい場合、CPUキャッシュ内にとどまり、パフォーマンスを向上させることができます。多数の小さなオブジェクトを作成および破棄する場合、マネージヒープを維持するオーバーヘッドは、C++ランタイムに必要なものよりも小さくなる可能性があります。繰り返しますが、これはアプリケーションに大きく依存しています。 研究Pythonパフォーマンス) 特定のマネージドPythonアプリケーションは結果としてコンパイルされたバージョンよりもはるかに優れたスケーリングが可能であることを示していますより最適なメモリアクセスパターン。
主な関心事は速度ではなく、Windowsのバージョンとアップグレード全体の安定性です。 Win32は、Windowsバージョン間でほとんど影響を受けないため、非常に安定しています。
サーバーが廃止され、ソフトウェアが移行されると、.Netを使用することで多くの不安が生じ、通常.NETバージョンに関する多くのパニックが発生しますが、10年前に構築されたWin32アプリケーションは何も起こらなかったように動作し続けます。
混乱させないでください!
C#アプリケーションが最適なケースで記述され、C++アプリケーションが最適なケースで記述されている場合、C++の方が高速です。
C#がJavaのJVMと同様の仮想マシンを使用するなど、C++が本質的にC#よりも高速である理由については、多くの理由があります。基本的に高レベルの言語は、パフォーマンスが低下します(最適な場合に使用する場合)。
あなたが経験豊富なプロのC++プログラマであるのと同じように経験豊富なプロのC#プログラマである場合、C#を使用したアプリケーションの開発はC++よりもはるかに簡単で高速です。
これらの状況の間に他の多くの状況が考えられます。たとえば、C#アプリケーションとC++アプリケーションを記述して、C#アプリがC++アプリよりも速く実行されるようにすることができます。
言語を選択するには、プロジェクトの状況とその主題に注意する必要があります。一般的なビジネスプロジェクトでは、C#を使用する必要があります。ビデオコンバーターや画像処理プロジェクトのような高パフォーマンスが必要なプロジェクトの場合は、C++を選択する必要があります。
OK。 C++の最も可能性のある速度がC#より速い理由について、いくつかの実際的な理由を比較してみましょう。適切に作成されたC#アプリケーションと同じC++バージョンを考えてみましょう。
専門家として実際にさらに情報を入手したい場合は、 こちらをご覧ください 。 Javaですが、C#にも適用されます。