C ++パフォーマンスとJava / C#


119

私の理解では、C / C ++は特定のマシンアーキテクチャで実行するネイティブコードを生成します。逆に、JavaやC#などの言語は、ネイティブアーキテクチャを抽象化する仮想マシン上で実行されます。この中間ステップのため、JavaまたはC#がC ++の速度に一致することは論理的に不可能であるように見えますが、最新のコンパイラー(「ホットスポット」)はこの速度に達するか、それを超えることさえあると言われています。

おそらく、これは言語の問題よりもコンパイラの問題のほうが多いですが、これらの仮想マシン言語の1つがネイティブ言語よりも優れたパフォーマンスを発揮する方法を誰でも簡単に英語で説明できますか?


JavaおよびC#は、実行時に利用可能なコードを使用して、アプリケーションが実際に実行される方法に基づいて最適化を行うことができます。たとえば、プログラムの実行中に実際に変更され、まだ正しい共有ライブラリのコードをインライン化できます。
Peter Lawrey、

これらの答えに非常にフレーク状の理論の多くを読む前にチェックするためのいくつかの実際の測定:shootout.alioth.debian.org/u32/...
Justicle

回答:


178

一般に、C#とJavaは、JITコンパイラー(ILを初めて実行するときにILをコンパイルするコンパイラー)が最適化を行うことができるため、C ++コンパイル済みプログラムでは実行できない最適化を行うことができるため、マシンにクエリを実行できるため、高速または高速になります。マシンがIntelかAMDかを判別できます。Pentium 4、Core Solo、またはCore Duo; またはSSE4などをサポートする場合

C ++プログラムは、すべてのマシンで適切に実行されるように、通常は混合最適化で事前にコンパイルする必要がありますが、単一の構成(つまり、プロセッサ、命令セット、その他のハードウェア)の場合ほど最適化されていません。

さらに、特定の言語機能により、C#およびJavaのコンパイラーは、C / C ++コンパイラーが行うのが安全ではない特定の部分を離れて最適化できるように、コードについて仮定を行うことができます。ポインタにアクセスできる場合、安全ではない最適化がたくさんあります。

また、JavaとC#は、ガベージコレクターとコードの間の抽象化のレイヤーにより、ヒープのすべての圧縮を一度に実行できるため(かなり高価な操作)、C ++よりも効率的にヒープの割り当てを行うことができます。

今、私はこの次の点でJavaについて話すことはできませんが、たとえばC#は、メソッドの本体が空であることを知っている場合、実際にメソッドとメソッド呼び出しを削除することを知っています。そして、コード全体でこの種のロジックを使用します。

ご覧のとおり、特定のC#またはJavaの実装が高速になる理由はたくさんあります。

これで、C ++で特に最適化できる特定の最適化を行うことができます。特にグラフィックス領域で、ハードウェアの近くにいるときはいつでも、C#で実行できることは何でも吹き飛ばされます。ポインタはここで不思議に思います。

だからあなたが書いているものに応じて、私はどちらかで行きます。ただし、ハードウェアに依存しないもの(ドライバー、ビデオゲームなど)を作成している場合は、C#のパフォーマンスについては心配しません(ここでもJavaについて話すことはできません)。うまくいきます。

Java側の1つである@Swatiは、優れた記事を指摘しています。

https://www.ibm.com/developerworks/library/j-jtp09275


あなたの推論は偽物です-C ++プログラムはターゲットアーキテクチャ用にビルドされます。実行時に切り替える必要はありません。
Justicle

3
@Justicleさまざまなアーキテクチャに対してc ++コンパイラが提供する最高のものは、通常、x86、x64、ARMなどです。これで、特定の機能(SSE2など)を使用するように指示できます。運が良ければ、その機能が利用できない場合にバックアップコードを生成することさえできますが、それは可能な限り細かいことです。確かに、キャッシュサイズなどに依存する特殊化はありません。
VOO

4
この理論起こらない例については、shootout.alioth.debian.org / u32 /…を参照してください。
Justicle '18

1
正直なところ、これは最悪の答えの1つです。それはとても根拠がないので、私はそれを反転させることができました。一般化が多すぎ、知識が多すぎる(空の関数を最適化することは、実際には氷山の一角にすぎません)。贅沢なC ++コンパイラには、次のようなものがあります。別の贅沢:チェックは強制されません。しかし、stackoverflow.com / questions / 145110 / c-performance-vs-java-c / …で詳細を確認してください。
Sebastian Mach

1
@OrionAdrianわかりました。これで完全な円になります... この理論が起こらない例については、shootout.alioth.debian.org / u32 /…を参照してください。言い換えれば、曖昧な投機的な発言をする前に、あなたの理論が正しいことが証明できることを示してください。
Justicle 2012年

197

JITと静的コンパイラ

以前の投稿ですでに述べたように、JITは実行時にIL /バイトコードをネイティブコードにコンパイルできます。そのコストは言及されましたが、結論には至りませんでした:

JITの大きな問題の1つは、すべてをコンパイルできないことです。JITのコンパイルには時間がかかるため、JITはコードの一部のみをコンパイルしますが、静的コンパイラは完全なネイティブバイナリを生成します。コンパイラーは単にJITを簡単に上回ります。

もちろん、C#(またはJava、またはVB)は通常、C ++よりも実行可能で堅牢なソリューションを生成する方が高速です(C ++に複雑なセマンティクスがあるためであり、C ++標準ライブラリは、興味深く強力ですが、完全版と比較するとかなり劣ります.NETまたはJavaの標準ライブラリのスコープ)なので、通常、C ++と.NETまたはJava JITの違いはほとんどのユーザーには見えません。重要なバイナリでは、C ++処理を呼び出すことができます。 C#またはJavaから(この種のネイティブコール自体が非常にコストがかかる場合でも)...

C ++メタプログラミング

通常は、C ++ランタイムコードを、C#またはJavaの同等のコードと比較していることに注意してください。ただし、C ++には、Java / C#よりも優れた機能が1つあります。それは、テンプレートメタプログラミングです。コード処理はコンパイル時に行われるため、コンパイル時間は大幅に増加し、ランタイムはゼロ(またはほぼゼロ)になります。

私はまだこれに実際の影響を見ています(私は概念のみで遊んでいましたが、それまでの違いは、JITの場合は実行の秒、C ++の場合はゼロでした)、ファクトテンプレートのメタプログラミングと並んで、これは言及する価値がありますささいな...

Edit 2011-06-10: C ++では、型の操作はコンパイル時に行われます。つまり、非ジェネリックコードを呼び出すジェネリックコードを生成します(たとえば、文字列から型Tへのジェネリックパーサー、認識される型Tの標準ライブラリAPIを呼び出します)。パーサーをユーザーが簡単に拡張できるようにすること)は非常に簡単で非常に効率的ですが、JavaまたはC#で同等のものを書くのはせいぜい苦痛であり、コンパイル時に型がわかっていても、実行時に常に遅くなり、解決されます。つまり、JITがすべてをインライン化すること唯一の希望です。

...

Edit 2011-09-20: Blitz ++(HomepageWikipedia)の背後にあるチームはそのようにして、明らかに、C ++テンプレートのメタプログラミングを介して、ランタイムの実行からコンパイル時間まで可能な限り移動することで、科学計算におけるFORTRANのパフォーマンスに到達することを目標としています。だから、私が上で書いた「私はこれに実際の生活の影響をまだ見ている」という部分は明らかに実際の生活に存在してます。

ネイティブC ++のメモリ使用量

C ++のメモリ使用量はJava / C#とは異なるため、異なる利点/欠点があります。

JIT最適化に関係なく、メモリへの直接ポインタアクセスほど高速なものはありません(プロセッサキャッシュなどを無視してください)。したがって、メモリ内に連続したデータがある場合、C ++ポインタ(つまり、Cポインタ... Caesarに期限を与えましょう)を介してデータにアクセスすると、Java / C#よりも数倍速くなります。また、C ++にはRAIIがあり、C#やJavaを使用するよりも多くの処理がはるかに簡単になります。C ++はusing、そのオブジェクトの存在をスコープする必要はありません。また、C ++にはfinally句がありません。これはエラーではありません。

:-)

また、C#プリミティブのような構造体にもかかわらず、C ++の「スタック上の」オブジェクトは、割り当てと破棄に費用がかからず、GCが独立したスレッドで動作してクリーニングを行う必要がありません。

メモリの断片化に関しては、2008年のメモリアロケータは1980年の古いメモリアロケータではなく、通常はGCと比較されます。C++の割り当てはメモリ内で移動できません。断片化が発生しない場合の最適化?適切なタスクに適切なアロケーターを使用することは、C ++開発者ツールキットの一部でなければなりません。さて、アロケータを書くのは簡単ではありません、そして、私たちのほとんどはもっと良いことをしています、そしてほとんどの場合、RAIIやGCは十分に優れています。

2011-10-04を編集:効率的なアロケーターの例:Windowsプラットフォームでは、Vista以降、低フラグメンテーションヒープがデフォルトで有効になっています。以前のバージョンの場合、WinAPI関数HeapSetInformationを呼び出すことにより、LFHをアクティブ化できます。他のOSでは、代替アロケーターが提供されています(https://secure.wikimedia.org/wikipedia/en/wiki/Malloc for a list)

現在、メモリモデルは、マルチコアおよびマルチスレッディングテクノロジの台頭により、やや複雑になっています。この分野では、.NETのほうが有利だと思います。一部の「ベアメタル」ハッカーが「マシンの近く」のコードを称賛するのは簡単です。しかし、今では、コンパイラーにその仕事を任せるよりも、手動でより良いアセンブリーを生成することはかなり困難です。C ++の場合、コンパイラは通常、10年前からハッカーよりも優れています。C#およびJavaの場合、これはさらに簡単です。

それでも、新しい標準C ++ 0xはC ++コンパイラに単純なメモリモデルを課し、C ++の効果的なマルチプロセッシング/並列/スレッド化コードを標準化(したがって単純化)し、コンパイラの最適化をより簡単かつ安全にします。しかし、その約束が守られているかどうかは、数年後にわかります。

C ++ / CLIとC#/ VB.NETの比較

注:このセクションでは、C ++ / CLI、つまりネイティブC ++ではなく.NETによってホストされるC ++について説明します。

先週、私は.NET最適化のトレーニングを受けましたが、いずれにせよ静的コンパイラーが非常に重要であることを発見しました。JITよりも重要です。

C ++ / CLI(またはその祖先、Managed C ++)でコンパイルされたまったく同じコードは、C#(またはVB.NET、コンパイラーがC#と同じILを生成)で生成された同じコードよりも数倍速い場合があります。

C ++スタティックコンパイラは、C#よりもすでに最適化されたコードを生成する方がはるかに優れていたためです。

たとえば、.NETでの関数のインライン展開は、バイトコードの長さが32バイト以下の関数に制限されています。そのため、C#の一部のコードは40バイトのアクセサーを生成しますが、これはJITによってインライン化されません。C ++ / CLIの同じコードは20バイトのアクセサーを生成し、これはJITによってインライン化されます。

別の例は一時変数で、C#コンパイラーによって生成されたILで言及されている間、C ++コンパイラーによって単純にコンパイルされます。C ++静的コンパイルの最適化により、コードが少なくなるため、より積極的なJIT最適化が再び許可されます。

この理由は、C ++ / CLIコンパイラーがC ++ネイティブコンパイラーの膨大な最適化技術から利益を得ているためと推測されていました。

結論

私はC ++が大好きです。

しかし、私が見る限り、C#やJavaの方が良いでしょう。C ++よりも高速なためではなく、それらの品質を合計すると、C ++よりも生産性が向上し、トレーニングの必要性が少なくなり、標準ライブラリが完全になるためです。そして、ほとんどのプログラムと同様に、それらの速度の違い(何らかの方法で)は無視できます...

編集(2011-06-06)

C#/。NETでの私の経験

私は5か月間、ほぼ独占的なプロ仕様のC#コーディングを行っています(これにより、CVとJavaで既にいっぱいになった私のCVと、C ++ / CLIが少し追加されます)。

WinForms(Ahem ...)とWCF(cool!)、WPF(Cool !!!! XAMLとraw C#の両方を使用しました。WPFは非常に簡単なので、Swingはそれに匹敵しないと思います)、およびC#4.0。

結論は、C ++よりもC#/ Javaで動作するコードを生成する方が簡単/高速ですが、C#で(そしてJavaでさらに)強力で安全で堅牢なコードを生成することは、C ++よりもはるかに難しいということです。理由はたくさんありますが、次のように要約できます。

  1. ジェネリックはテンプレートほど強力ではありません効率的なジェネリックParseメソッド(文字列からTまで)、またはC#のboost :: lexical_castの効率的な同等物を記述して問題を理解してください
  2. RAIIは比類のないままですGCは依然としてリークする可能性があり(そう、私はその問題を処理する必要がありました)、メモリのみを処理します。using正しいDispose実装の作成が難しいため、C#でも簡単で強力ではありません
  3. C#のreadonlyとJavaはfinalC ++のようにどこに有用としてではないconstそれが内蔵されている間C ++の特徴あなたは途方もない作業をせずにC#で読み取り専用の複雑なデータ例えばノードの(ツリー)公開することができる方法は、ありません。不変データは興味深いソリューションです、しかしすべてが不変にできるわけではないので、はるかに十分ではありません)。

したがって、C#は、何かが機能することを望む限り快適な言語であり続けますが、常に安全に機能するものを欲するときは苛立たしい言語です。

C#と同じ問題があるため、Javaはさらにイライラします。C#のusingキーワードに相当するものがないため、非常に熟練した私の同僚は、リソースが適切に解放されることを確認するのに多くの時間を費やしましたが、C ++の相当するものは(デストラクタとスマートポインタを使用して)簡単になりました。

したがって、C#/ Javaの生産性向上はほとんどのコードで目に見えると思います...コードをできる限り完璧にする必要がある日までは。その日、あなたは痛みを知るでしょう。(あなたは私たちのサーバーとGUIアプリから尋ねられたものを信じられないでしょう...)。

サーバー側のJavaおよびC ++について

私は建物の反対側でサーバーチーム(GUIチームに戻る前に2年間働いた)と連絡を取り続け、興味深いことを学びました。

昨年、Javaには多くのフレームワーク/ツールがあり、メンテナンス、デプロイなどが簡単であるため、Javaサーバーアプリは古いC ++サーバーアプリに置き換わる傾向にありました。

...低レイテンシの問題が最後の数ヶ月で醜い頭を育てるまで。次に、熟練したJavaチームが試みた最適化に関係なく、Javaサーバーアプリは、実際には最適化されていない古いC ++サーバーとの競争を単純かつ完全に失いました。

現在、決定は、パフォーマンスが依然として重要であり、低レイテンシの目標に関心がない場合にJavaサーバーを一般的に使用するように維持し、低レイテンシと超低レイテンシのニーズに合わせてすでに高速のC ++サーバーアプリケーションを積極的に最適化することです。

結論

予想されるほど単純なものはありません。

Java、さらに多くのC#は、高速なコーディングが可能で、すぐに結果が得られる広範な標準ライブラリとフレームワークを備えたクールな言語です。

しかし、生のパワー、強力で体系的な最適化、強力なコンパイラサポート、強力な言語機能、絶対的な安全性が必要な場合、JavaとC#は、競争力を維持するために必要な最後の欠けているが重要な品質の品質を獲得することを困難にします。

平均品質のコードを作成するためにC ++ / JavaよりもC#/ Javaの開発者の方が時間と経験の少ない開発者を必要とするかのようですが、一方で、完璧で高品質のコードが必要になった瞬間、結果を得るのが突然簡単かつ迅速になりました。まさにC ++で。

もちろん、これは私自身の認識であり、おそらく私たちの特定のニーズに限定されています。

しかし、それでも、今日はGUIチームとサーバー側チームの両方で起こっています。

もちろん、何か新しいことが起こったらこの投稿を更新します。

編集(2011-06-22)

「パフォーマンスに関しては、C ++が大差で勝っていることがわかりました。しかし、C ++には最も広範なチューニング作業も必要でした。その多くは、平均的なプログラマーには利用できない高度なレベルで行われました。

[...] Javaバージョンはおそらく実装が最も簡単でしたが、パフォーマンスを分析するのが最も困難でした。具体的には、ガベージコレクションに関する影響は複雑で、調整が非常に困難でした。」

出典:

編集(2011-09-20)

「Facebookでの今後の言葉は、「合理的に書かれたC ++コードは高速に実行されるだけである」ということです。これは、PHPおよびJavaコードの最適化に費やされた膨大な労力を強調しています。逆説的に、C ++コードは他の言語よりも書くのが難しいですが、効率的なコードは[他の言語よりもC ++で書く方が]ずっと簡単です。 "

// build /のHerb SutterAndrei Alexandrescuを引用

出典:


8
C#の5か月後に編集すると、私自身の経験が正確に説明されます(テンプレートの方が良い、constの方が良い、RAII)。+1。これら3つは、C ++(または、まだ時間を割いていないD)の私の個人的なキラー機能として残っています。
Sebastian Mach

「コード処理はコンパイル時に行われます」。したがって、テンプレートメタプログラミングはプログラムでのみ機能し、コンパイル時に利用できますが、これは多くの場合当てはまりません。たとえば、ランタイムコード生成ができないため、バニラC ++で競争力のある正規表現ライブラリを作成することはできません(重要な側面)。メタプログラミング)。
JD

「型での再生はコンパイル時に行われます。JavaまたはC#での同等の処理は、作成するのがせいぜい苦痛であり、コンパイル時に型がわかっていても、実行時に常に遅くなり、解決されます。」C#では、これは参照型にのみ当てはまり、値型には当てはまりません。
JD

1
「JIT最適化に関係なく、メモリへの直接ポインタアクセスほど高速なものはありません...メモリに連続したデータがある場合、C ++ポインタ(つまり、Cポインタ... Caesarに期限を与えましょう)を介してアクセスします。 Java / C#よりも高速です。」ポインタはエイリアシング関連の最適化を妨げるため、SciMark2ベンチマークからのSORテストでJavaがC ++を打ち負かしているのを人々は見ています。blogs.oracle.com/dagastine/entry/sun_java_is_faster_than
JD

また、.NETはリンク後に動的にリンクされたライブラリ全体でジェネリックの型特殊化を行うのに対し、C ++ではテンプレートをリンクする前に解決する必要があるため、特殊化できないことにも注意してください。そして明らかに、ジェネリックがテンプレートよりも優れている大きな利点は、わかりやすいエラーメッセージです。
JD

48

管理されたパフォーマンスと管理されていないパフォーマンスについて話すときはいつでも、Rico(およびRaymond)が中国語/英語辞書のC ++バージョンとC#バージョンを比較したシリーズを指摘したいと思います。このグーグル検索はあなたが自分で読むことを可能にしますが、私はリコの要約が好きです。

だから私は私の打ちのめされた敗北に恥じていますか?ほとんどありません。マネージコードはほとんど労力を費やさずに非常に良い結果を得ました。マネージドレイモンドを倒すには:

  • 自分のファイルI / Oを書く
  • 彼自身の文字列クラスを書く
  • 彼自身のアロケーターを書く
  • 彼自身の国際地図を書く

もちろん、彼はこれを行うために利用可能な低レベルのライブラリを使用しましたが、それはまだ多くの仕事です。STLプログラムに残っているものを呼び出すことができますか?私はそうは思いません、彼はstd :: vectorクラスを保持したと思います。これは最終的に問題になることはなく、彼はfind関数を保持しました。他のほとんどすべてがなくなっています。

つまり、間違いなく、CLRを打ち負かすことができます。レイモンドは彼のプログラムをさらに速くすることができると思います。

興味深いことに、両方のプログラムの内部タイマーによって報告されたファイルを解析する時間はほぼ同じで、それぞれ30ミリ秒です。違いはオーバーヘッドです。

私にとっての一番下の行は、アンマネージバージョンが元のアンマネージコードの単純な移植であったマネージバージョンに勝つには、6つのリビジョンが必要だったということです。パフォーマンスの最後のすべてのビットが必要な場合(そしてそれを取得するための時間と専門知識がある場合)、管理されない状態にする必要がありますが、私にとっては、最初のバージョンで33を超える桁違いの利点を利用します%6回試すと得られます。


3
リンクが死んでいる、ここに記載されている記事が見つかりました:blogs.msdn.com/b/ricom/archive/2005/05/10/416151.aspx
gjvdkamp

まず、Raymond Chenのコードを見ると、C ++やデータ構造をよく理解していないことは明らかです。彼のコードは、Cコードにパフォーマンス上の利点がない場合でも、低レベルのCコードにほぼ直結します(これは、一種の不信感であり、プロファイラーの使用方法に関する知識の欠如のようです)。彼はまた、辞書を実装する最もアルゴリズム的に健全な方法を理解できませんでした(彼はキリストのためにstd :: findを使用しました)。Java、Python、C#などについて何か良い点がある場合-それらはすべて非常に効率的な辞書を提供します...
stinky472

試行またはstd :: mapは、C ++またはハッシュテーブルに向かってはるかに有利です。最後に、辞書はまさに、高レベルのライブラリとフレームワークから最も恩恵を受けるプログラムのタイプです。含まれているライブラリほど言語の違いを示していません(このうち、C#の方がはるかに完全であり、タスクに適したツールがはるかに多いと思います)。大規模な行列/ベクトルコードのように、メモリの大きなブロックを比較して操作するプログラムを示します。この場合のように、コーダーが何を知らない場合でも、これは非常に迅速に解決します...
stinky472

26

特定のCPU最適化のためのコンパイルは通常過大評価されています。C ++でプログラムを取得し、pentium PROの最適化を使用してコンパイルして、pentium 4で実行します。次に、pentium 4の最適化を使用して再コンパイルします。いくつかのプログラムでそれを実行している長い午後に合格しました。一般的な結果?通常、パフォーマンスの向上は2〜3%未満です。したがって、理論上のJITの利点はほとんどありません。パフォーマンスのほとんどの違いは、スカラーデータ処理機能を使用しているときにのみ観察できます。いずれにせよ、最大のパフォーマンスを達成するには、最終的に手動で微調整する必要があります。この種の最適化は実行に時間がかかりコストがかかり、JITに適さない場合があります。

実際のアプリケーションと実際のアプリケーションでは、C ++は通常、Javaよりも高速ですが、これは主に、メモリフットプリントが軽く、キャッシュパフォーマンスが向上するためです。

しかし、すべてのC ++機能を使用するには、開発者は一生懸命努力する必要があります。優れた結果を得ることができますが、そのためには脳を使用する必要があります。C ++は、より多くのツールを提供することを決定した言語であり、言語を上手に使用できるようにするには、ツールを学習する必要があるという代価がかかります。


4
CPU最適化のためにコンパイルすることはそれほど多くありませんが、ランタイムパス最適化のためにコンパイルします。メソッドが特定のパラメーターで頻繁に呼び出されることがわかった場合は、そのパラメーターを定数として使用してルーチンをプリコンパイルし、(フローを制御するブール値の場合)膨大な作業を除外することができます。C ++は、この種の最適化を行うことはできません。
ビルK

1
では、JITは、ルーチンを再コンパイルして、監視されたランパスを利用する方法を教えてください。それによって、どの程度の違いが生まれますか?
David Thornley、

2
@Bill私は2つのことを混合している可能性があります...しかし、命令パイプラインで実行時に分岐予測が行われて、言語に関係なく同様の目標が達成されませんか?
Hardy

@ハードはい、CPUは言語に関係なく分岐予測を実行できますが、ループが何にも影響を与えないことを観察してループ全体を除外することはできません。また、mult(0)が0を返すようにハードワイヤードされていることは観察されず、メソッド呼び出し全体をif(param == 0)result = 0に置き換えるだけです。そして、関数/メソッド呼び出し全体を避けます。コンパイラが何が起こっているかの包括的な概要を持っている場合、Cはこれらのことを行うことができますが、通常、コンパイル時に十分な情報がありません。
Bill K

21

JIT(ジャストインタイムコンパイル)は、ターゲットプラットフォーム用に最適化されるため、非常に高速です。

これは、開発者がコードを書いたCPUに関係なく、CPUがサポートできるコンパイラトリックを利用できることを意味します。

.NET JITの基本概念は次のように機能します(大幅に簡略化されています)。

初めてメソッドを呼び出す:

  • プログラムコードがメソッドFoo()を呼び出す
  • CLRはFoo()を実装する型を調べ、それに関連付けられているメタデータを取得します
  • CLRはメタデータから、IL(中間バイトコード)が格納されているメモリアドレスを認識します。
  • CLRはメモリのブロックを割り当て、JITを呼び出します。
  • JITはILをネイティブコードにコンパイルし、割り当てられたメモリに配置してから、Foo()の型メタデータの関数ポインターをこのネイティブコードを指すように変更します。
  • ネイティブコードが実行されます。

2回目のメソッドの呼び出し:

  • プログラムコードがメソッドFoo()を呼び出す
  • CLRはFoo()を実装する型を調べ、メタデータで関数ポインターを見つけます。
  • このメモリ位置のネイティブコードが実行されます。

ご覧のとおり、2回目は、リアルタイム最適化の利点を除いて、C ++とほぼ同じプロセスです。

とは言っても、マネージ言語を遅くするオーバーヘッドの問題は他にもありますが、JITは非常に役立ちます。


ちなみにジョナサン、誰かがまだあなたのことを反対投票していると思います。私があなたに賛成票を投じたとき、あなたはこの投稿に-1を持っていました。
ブライアンR.ボンディ

12

私はオリオンエイドリアンの答えが好きですが、それには別の側面があります。

同じ質問が数十年前にアセンブリ言語とFORTRANのような「人間の」言語について提起されました。そして答えの一部は似ています。

はい、C ++プログラムは、特定の(自明ではない)アルゴリズムでC#よりも高速である可能性がありますが、C#のプログラムは、多くの場合、C ++の「素朴な」実装、およびC ++の最適化バージョンと同じくらい高速または高速です。開発には時間がかかりますが、C#バージョンよりもわずかなマージンで勝る可能性があります。それで、それは本当に価値がありますか?

その質問に1つずつ答える必要があります。

そうは言っても、私はC ++の長い間ファンであり、それは信じられないほど表現豊かで強力な言語だと思います。しかし、多くの「現実の」問題(個人的には、「解決するために支払われる種類」を意味します)では、C#はより早く、より安全に仕事を完了します。

あなたが支払う最大のペナルティ?多くの.NETおよびJavaプログラムはメモリを大量に消費します。同じような複雑さのC ++プログラムが「数十」MBをかろうじてスクラッチするとき、.NETおよびJavaアプリが「数百」メガバイトのメモリを消費するのを見てきました。


7

Hotspotを使用した場合でも、JavaコードがC ++よりも高速に実行されることがどのくらいの頻度で見つかるかはわかりませんが、どのようにして発生するかを説明します。

コンパイルされたJavaコードは、JVMの解釈されたマシン言語と考えてください。Hotspotプロセッサは、コンパイルされたコードの特定の部分が何度も使用されることに気づくと、マシンコードの最適化を実行します。アセンブリを手動で調整することは、ほとんどの場合C ++でコンパイルされたコードよりも高速であるため、プログラムで調整されたマシンコードがそれほど悪くないことは理解できます。

したがって、非常に繰り返しの多いコードの場合、ガベージコレクションが登場するまでは、Hotspot JVMがJavaをC ++よりも高速に実行できる可能性があることがわかりました。:)


アサーションについて詳しく教えてくださいSince hand-tuning Assembly is almost always faster than C++ compiled code。「アセンブリの手動調整」と「C ++コンパイル済みコード」とはどういう意味ですか?
paercebal 2011

まあ、それはコンパイラのオプティマイザがルールに従うという考え方に基づいていますが、コーダはそうではありません。したがって、オプティマイザが完全に最適化できないとコードが常に判断する可能性がありますが、人間は全体像を調べるか、コードが実際に何をしているかを知ることによって可能です。これは3年前のコメントであり、HotSpotについては以前よりも知っています。動的最適化がコードをより高速に実行するための非常に良い方法であることが簡単にわかります。
billjamesdev

1. Hotspotまたはその他のJITによる最適化は、コンパイラーの最適化のままです。JITは、一部の結果(コードが頻繁に呼び出される)をインライン化できる、または実行中のプロセッサに基づいて最適化を行うことができるという静的コンパイラよりも優れていますが、それでもコンパイラの最適化です。。。2.あなたは「アセンブリの微調整」ではなく、アルゴリズムの最適化について話していると思います。「人間のコーダーによる手動アセンブリの微調整」は、10年以上前からコンパイラの最適化よりも優れた結果を生み出すことができませんでした。実際、アセンブリを操作する人間は通常、最適化を
台無しにし

「静的最適化」ではなく「コンパイラ最適化」という誤った用語を使用していることがわかりました。少なくともゲーム業界では、最近のPS2と同じように、コンソールにあることがわかっている特定のチップを「最適化」するために、場所にハンドコーディングされたアセンブリをまだ使用していたことを指摘しておきます。これらの新しいチップ用のクロスコンパイラは、x86アーキテクチャ用のものほど洗練されていません。バック上記元の質問に:JITは良いこと(TM)である、前の最適化を測定することができるという利点を有する
billjamesdev

C / C ++はカットしないので、ほとんどのプロダクションGCも手書きのアセンブラーを使用することに注意してください。
JD 2011

6

一般に、プログラムのアルゴリズムは、言語よりもアプリケーションの速度にとって非常に重要です。C ++を含め、どの言語でも貧弱なアルゴリズムを実装できます。それを念頭に置いて、一般的には、より効率的なアルゴリズムの実装に役立つ言語でコードをより速く実行できるようになります。

高水準言語は、多くの効率的な事前に作成されたデータ構造へのより簡単なアクセスを提供し、非効率的なコードを回避するのに役立つ推奨プラクティスにより、この点で非常にうまく機能します。もちろん、非常に低速なコードの束を簡単に作成できることもあります。そのため、プラットフォームを知る必要があります。

また、C ++はSTLコンテナー、自動ポインターなどの「新しい」(引用符に注意)機能に追いついています。たとえば、boostライブラリを参照してください。また、一部のタスクを実行する最速の方法には、高水準言語では禁止されているポインター演算などの手法が必要な場合があります。ただし、通常、必要に応じて実装できる言語で作成されたライブラリを呼び出すことができます。 。

主なことは、使用している言語、それに関連付けられているAPI、それが実行できること、およびその制限について理解することです。


5

私も知りません...私のJavaプログラムは常に遅いです。:-)ただし、C#プログラムが特に遅いことに気づいたことはありません。


4

これは、自分のコンピューターで自分で試すことができる別の興味深いベンチマークです。

ASM、VC ++、C#、Silverlight、Javaアプレット、Javascript、Flash(AS3)を比較します

Roozzプラグインの速度デモ

javascriptの速度は、実行しているブラウザによって大きく異なります。これらのプラグインはホスティングブラウザーと同じプロセスで実行されるため、FlashとSilverlightについても同様です。しかし、Roozzプラグインは、独自のプロセスで実行される標準の.exeファイルを実行するため、速度はホスティングブラウザーの影響を受けません。


4

「より良いパフォーマンスを..」と定義する必要があります。まあ、私は知っています、あなたは速度について尋ねましたが、それが重要であるすべてではありません。

  • 仮想マシンは実行時のオーバーヘッドを増やしますか?はい!
  • 彼らはより多くのワーキングメモリを食べますか?はい!
  • 起動コストが高くなりますか(実行時の初期化とJITコンパイラ)?はい!
  • 巨大なライブラリをインストールする必要がありますか?はい!

など、そのバイアス、はい;)

C#とJavaを使用すると、得られるもの(より高速なコーディング、自動メモリ管理、大きなライブラリなど)に料金を支払います。しかし、あなたは詳細について交渉する余地があまりありません:完全なパッケージを取るか、何もしません。

これらの言語が一部のコードを最適化してコンパイル済みコードよりも高速に実行できるとしても、アプローチ全体は(IMHO)非効率的です。毎日トラックで職場まで5マイル運転することを想像してみてください。その快適さ、心地よさ、安全性(非常にしわくちゃなゾーン)、そしてしばらくの間ガスに乗った後は、標準的な車と同じくらい高速になります!なぜ私たちは皆、運転するために運転するトラックを持っていないのですか?;)

C ++では、あなたが支払うものを手に入れます。

ビャーネ・ストロヴストルップの引用:「それはそう少しごみ生成するので、C ++が私のお気に入りのごみ収集の言語である」 リンクテキストを


まあ、私は彼がその欠点について良い考えを持っていると思います、と彼はまた言いました: "Cは足で自分を撃つことを簡単にします; C ++はそれを難し​​くします、しかしそれをするときあなたの足全体を吹き飛ばします";)
Frunsi

「巨大なライブラリをインストールする必要がありますか」Javaは、プロジェクトジグソーでこの問題に対処していると思います。
toc777

「C ++では、あなたが支払うものを手に入れることができます。カウンターの例:既存のセットを再利用するために、追加されている要素がすでに存在する場合、再帰から長時間ジャンプするために例外を使用するOCamlおよびC ++(GNU GCC)のRBツリー実装をベンチマークしました。OCamlはC ++よりも最大6倍高速でした。これは、スタックが展開されるときにデストラクタのチェックに費用がかからないためです。
JD 2011

3
@ジョン:しかし、ある時点(後で?)では、とにかくオブジェクトを破棄する必要があります(少なくともメモリを解放する必要があります)。また、例外は例外的なケースであることに注意してください。少なくともC ++では、このルールを尊重する必要があります。例外が発生すると、C ++例外は重くなる可能性がありますが、これはトレードオフです。
フルンシ2011

@ジョン:timesシェルでベンチマークを繰り返してみてください。そのため、単一の側面だけでなく、プログラム全体をチェックします。結果は同じですか?
フルンシ2011

3

JavaまたはC#コンパイラーから生成された実行可能コードは解釈されません-「ジャストインタイム」(JIT)のネイティブコードにコンパイルされます。そのため、実行中に初めてJava / C#プログラムのコードが検出されると、「ランタイムコンパイラ」(別名JITコンパイラ)がバイトコード(Java)またはILコード(C#)をネイティブマシン命令に変換するため、オーバーヘッドが発生します。ただし、アプリケーションの実行中に次にそのコードが検出されると、ネイティブコードがすぐに実行されます。これは、一部のJava / C#プログラムが最初は遅いように見えますが、実行時間が長くなるほどパフォーマンスが向上することを説明しています。良い例がASP.Net Webサイトです。Webサイトに初めてアクセスしたときは、JITコンパイラーによってC#コードがネイティブコードにコンパイルされるため、少し遅くなることがあります。


3

あなたが尋ねた特定の質問についてここにいくつかの良い答えがあります。一歩下がって全体像を見たいと思います。

作成するソフトウェアの速度に対するユーザーの認識は、codegenが最適化するだけでなく、他の多くの要因の影響を受けることに注意してください。ここではいくつかの例を示します。

  • 手動でのメモリ管理は正しく行うことは困難であり(リークなし)、効率的に行うことはさらに困難です(使い終わったらすぐにメモリを解放します)。一般に、GCを使用すると、メモリを適切に管理するプログラムが生成される可能性が高くなります。GCをしのぐために、非常に一生懸命作業し、ソフトウェアの配信を遅らせてもよろしいですか?

  • C#は、C ++よりも読みやすく、理解しやすいです。また、C#コードが正しく機能していることを確信させる方法が他にもあります。つまり、バグを導入するリスクを抑えてアルゴリズムを最適化できるということです(ユーザーは、たとえソフトウェアがクラッシュしても、クラッシュするソフトウェアが嫌いです!)

  • ソフトウェアをC ++よりもC#で高速に作成できます。これにより、時間を空けてパフォーマンスに取り組むことができ、ソフトウェアを時間どおりに提供できます。

  • C#では、C ++よりも優れたUIを書く方が簡単です。そのため、UIの応答性を維持しながら作業をバックグラウンドにプッシュしたり、プログラムがしばらくブロックしなければならないときに進行状況やハートビートUIを提供したりすることができます。これは何も速くはしませんが、ユーザーが待つことを幸せにします。

C#について私が言ったことはすべてJavaに当てはまると思いますが、確かに言う経験はありません。


3

C ++を学ぶJava / C#プログラマーであれば、Java / C#の観点から考え続け、逐語的にC ++構文に変換したくなるでしょう。その場合、ネイティブコードとインタプリタ/ JITの前述の利点のみが得られます。C ++対Java / C#で最大のパフォーマンス向上を得るには、C ++で考えることを学び、コードを設計して、C ++の長所を活用する必要があります。

Edsger Dijkstraを言い換えると、[最初の言語]は回復を超えて心を傷つけます。Jeff Atwood
を言い換えると、[第一言語]を新しい言語で書くことができます。


1
「どの言語でもFORTRANを書くことができる」ということばは、ジェフのキャリアよりも前からあると思います。
David Thornley、

3

最も重要なJIT最適化の1つは、メソッドのインライン化です。実行時の正確さを保証できる場合、Javaは仮想メソッドをインライン化することもできます。この種の最適化は通常、プログラム全体を分析する必要があるため、標準の静的コンパイラでは実行できません。これは、個別のコンパイルのために困難です(対照的に、JITはすべてのプログラムを利用できます)。メソッドのインライン化により他の最適化が改善され、より大きなコードブロックが最適化されます。

Java / C#の標準メモリ割り当ても高速で、割り当て解除(GC)もそれほど遅くはありませんが、確定性は低くなります。


も確定的freedeleteはないことに注意してください。GCは割り当てないことで確定的にすることができます。
JD

3

仮想マシン言語はコンパイルされた言語をしのぐ可能性は低いですが、(少なくとも)次の理由により(C#を実行したことがないので、ここではJavaについて話しています)、問題にならないほど十分近づくことができます。

1 / Javaランタイム環境は通常、頻繁に実行されるコードを検出し、それらのセクションのジャストインタイム(JIT)コンパイルを実行できるため、将来的には完全なコンパイル速度で実行できます。

2 / Javaライブラリーの広大な部分がコンパイルされるため、ライブラリー関数を呼び出すと、解釈されずにコンパイル済みコードが実行されます。OpenJDKをダウンロードすると、コード(C)を表示できます。

3 /大規模な計算を行わない限り、プログラムが実行されているほとんどの時間は、非常に遅い(比較的話す)人間からの入力を待機しています。

4 / Javaバイトコードの検証の多くはクラスのロード時に行われるため、ランタイムチェックの通常のオーバーヘッドが大幅に削減されます。

5 /最悪の場合、パフォーマンスを重視するコードをコンパイルされたモジュールに抽出し、Java(JNIを参照)から呼び出して、フルスピードで実行することができます。

要約すると、Javaバイトコードがネイティブマシン言語を上回ることはありませんが、これを軽減する方法があります。ジャワの大きな利点は(私はそれを見るように)ある巨大な標準ライブラリとのクロスプラットフォームな性質。


1
項目2について、「2 / JavaライブラリのVast部分はコンパイルされているため、ライブラリ関数を呼び出すと、コンパイルされたコードが実行され、解釈されません」:これについての引用はありますか?それが本当にあなたの説明どおりだったとしたら、デバッガーからネイティブコードに頻繁に遭遇すると思いますが、そうではありません。
cero

Re:cero Debuggerは、あまり効率的ではないがより表現力豊かなパスを使用することが多いため、パフォーマンスに関連する何かの良いマーカーにはなりません。
Guvante、2008年

2
このHUGHライブラリには、もう1つの大きなパフォーマンスの向上があります。ライブラリコードは、多くのプログラマが自分で(限られた時間と専門知識の欠如を前提として)独自に作成するものやJavaで作成するものよりも優れていると思われます。図書館。
Liran Orevi 2009

3

Orion Adrian、C ++についても多くのことが言えるので、あなたの発言がどれほど根拠のないものであるかを確認するために、投稿を反転させてください。そして、Java / C#コンパイラが空の関数を最適化して削除すると言うと、あなた私の最適化の専門家ではないように聞こえます。理由は、a)本当に悪いレガシーコードを除いて、実際のプログラムに空の関数が含まれるべき理由、b)本当にそうではないからです。黒と最先端の最適化。

そのフレーズとは別に、あなたはポインターについて露骨に発言しましたが、JavaおよびC#のオブジェクトは基本的にC ++ポインターのように機能しませんか?それらは重ならないのでしょうか?それらがnullにならないようにできますか?C(およびほとんどのC ++実装)にはrestrictキーワードがあり、どちらにも値タイプがあり、C ++にはnull以外の保証付きの値への参照があります。JavaとC#は何を提供していますか?

>>>>>>>>>>

一般に、CおよびC ++は、AOTコンパイラ(ハイメモリの多数のコアビルドサーバーに展開前にコードをコンパイルするコンパイラ)がC#コンパイル済みプログラムよりも最適化できるため、同じくらい高速または高速です。それにはそうする時間がたくさんあるからです。コンパイラーは、マシンがIntelかAMDかを判別できます。Pentium 4、Core Solo、またはCore Duo; または、SSE4などをサポートしていて、コンパイラがランタイムディスパッチをサポートしていない場合は、いくつかの特殊なバイナリをデプロイすることで、自分で解決できます。

C#プログラムは通常、実行時にコンパイルされ、すべてのマシンで適切に実行されますが、単一の構成(つまり、プロセッサ、命令セット、その他のハードウェア)の場合ほど最適化されておらず、ある程度の時間を費やす必要があります。最初。ループ分裂、ループ反転、自動ベクトル化、プログラム全体の最適化、テンプレート拡張、IPOなどの機能は、エンドユーザーを困らせない方法ですべてを完全に解決することは非常に困難です。

さらに、特定の言語機能により、C ++またはCのコンパイラーは、Java / C#コンパイラーが安全ではない特定の部分を最適化できるようにコードについての仮定を行うことができます。ジェネリックの完全な型IDまたは保証されたプログラムフローにアクセスできない場合、安全ではない多くの最適化があります。

また、C ++とCは、レジスタを1つインクリメントするだけで一度に多くのスタック割り当てを実行します。これは、ガベージコレクタとコードの間の抽象化の層に関して、JavaやC#の割り当てよりも確かに効率的です。

今、私はこの次の点でJavaについて話すことはできませんが、たとえばC ++コンパイラーは、メソッドの本体が空であることがわかっている場合、実際にメソッドとメソッド呼び出しを削除し、一般的な部分式を排除し、再試行する場合があることを知っています最適なレジスタの使用法を見つけるために、境界チェックを強制せず、ループと内部ループを自動ベクトル化し、内部から外部に反転し、条件からループの外に移動し、ループを分割および分割解除します。Cの方法で行うように、std :: vectorをネイティブのゼロオーバーヘッド配列に展開します。手続き間の最適化を行います。呼び出し元サイトで直接戻り値を構築します。式を折りたたみ、伝達します。キャッシュに適した方法でデータを並べ替えます。ジャンプスレッディングを行います。ランタイムオーバーヘッドがゼロのコンパイル時レイトレーサーを作成できます。それは非常に高価なグラフベースの最適化を行います。特定のコードを構文的に完全に等しくないが意味的に同等のコードに置き換えれば、強度が低下します(古い「xor foo、foo」は、このような種類の古い最適化ですが、最も単純です)。親切にお願いしたい場合は、IEEE浮動小数点標準を省略して、浮動小数点オペランドの並べ替えなど、さらに多くの最適化を有効にすることができます。コードをマッサージして大虐殺した後、特定の最適化がより確実な最適化の基礎を築くことが多いため、プロセス全体を繰り返す可能性があります。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。特定のコードを構文的に完全に等しくないが意味的に同等のコードに置き換えますか?(古い「xor foo、foo」は、そのような種類の古い最適化ですが、最も単純です)。親切にお願いしたい場合は、IEEE浮動小数点標準を省略して、浮動小数点オペランドの並べ替えなど、さらに多くの最適化を有効にすることができます。コードをマッサージして大虐殺した後、特定の最適化がより確実な最適化の基礎を築くことが多いため、プロセス全体を繰り返す可能性があります。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。特定のコードを構文的に完全に等しくないが意味的に同等のコードに置き換えますか?(古い「xor foo、foo」は、そのような種類の古い最適化ですが、最も単純です)。親切にお願いしたい場合は、IEEE浮動小数点標準を省略して、浮動小数点オペランドの並べ替えなど、さらに多くの最適化を有効にすることができます。コードをマッサージして大虐殺した後、特定の最適化がより確実な最適化の基礎を築くことが多いため、プロセス全体を繰り返す可能性があります。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。親切にお願いしたい場合は、IEEE浮動小数点標準を省略して、浮動小数点オペランドの並べ替えなど、さらに多くの最適化を有効にすることができます。コードをマッサージして大虐殺した後、特定の最適化がより確実な最適化の基礎を築くことが多いため、プロセス全体を繰り返す可能性があります。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。親切にお願いしたい場合は、IEEE浮動小数点標準を省略して、浮動小数点オペランドの並べ替えなど、さらに多くの最適化を有効にすることができます。コードをマッサージして大虐殺した後、特定の最適化がより確実な最適化の基礎を築くことが多いため、プロセス全体を繰り返す可能性があります。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。また、シャッフルされたパラメーターで再試行して、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。また、シャッフルされたパラメーターを使用して再試行し、他のバリアントが内部ランキングでどのようにスコアリングするかを確認することもできます。そして、コード全体でこの種のロジックを使用します。

ご覧のとおり、特定のC ++またはCの実装が高速になる理由はたくさんあります。

これで、C ++で多くの最適化を行うことができます。特にCrunching、Realtime、Close-to-Metalレルムなど、C#でできることは何でも吹き飛ばすことができますが、それだけではありません。長い道のりを進むために、単一のポインターに触れる必要すらありません。

だからあなたが書いているものに応じて、私はどちらかで行きます。ただし、ハードウェアに依存しないもの(ドライバー、ビデオゲームなど)を作成している場合は、C#のパフォーマンスについては心配しません(ここでもJavaについて話すことはできません)。うまくいきます。

<<<<<<<<<<

一般的に、特定の一般化された議論は特定の投稿ではクールに聞こえるかもしれませんが、一般的に確かに信頼できるようには聞こえません。

とにかく、平和を作るために:AOTは素晴らしいです、そしてJITもそうです。正しい答えは次のとおりです。そして、本当の賢い人々は、あなたがとにかく両方の長所を利用できることを知っています。


2

Javaインタプリタは、実際にされたマシンコード生成された場合にのみ起こるのか、より良いあなたのコンパイラはあなたがC ++コードはJavaと解釈コストより遅い時点に、書いているC ++コードを生成しているマシンコードよりも最適化されています。

ただし、実際に発生する確率はかなり低くなります。おそらく、Javaに非常によく書かれたライブラリがあり、独自に作成したC ++ライブラリがない場合は除きます。


また、ある程度の言語の重みもあると思います。抽象度が低く、低いレベルで作業する場合は、より高速なプログラムを開発することになります。これは、バイトコードの実行自体に関するポイントとは無関係です。
ブライアンR.ボンディ

2

実際、C#は実際にはJavaのように仮想マシンで実行されません。ILは、完全にネイティブコードであり、ネイティブコードと同じ速度で実行されるアセンブリ言語にコンパイルされます。.NETアプリケーションを事前JITして、JITコストを完全に削除してから、完全にネイティブコードを実行することができます。

。アプリケーションの構築だけでなく、コストもかかります。C ++プログラムでもこれらすべてを実行できることに注意してください(コア.NET機能の多くは実際には.NETコードであり、ROTORで表示できます)。ただし、同じ機能を手動で記述した場合、.NETランタイムが最適化され、細かく調整されているため、プログラムが非常に遅くなる可能性があります。

とはいえ、マネージコードの強みの1つは、完全に検証可能であることです。コードを実行する前に、コードが別のプロセスのメモリにアクセスしたり、アンセージ処理を行ったりしないことを確認できます。マイクロソフトは、完全に管理されたオペレーティングシステムの調査プロトタイプを持っていますが、100%管理された環境は、この検証を利用して、管理されたプログラムで不要になったセキュリティ機能をオフにすることにより、最新のオペレーティングシステムよりも実際にはるかに高速に実行できることが驚くべきことを示しています(場合によっては10倍のように話しています)。SEラジオには、このプロジェクトについて語る素晴らしいエピソードがあります。


1

場合によっては、マネージコードは実際にはネイティブコードより高速です。たとえば、「マークアンドスイープ」ガベージコレクションアルゴリズムを使用すると、JREやCLRなどの環境で、多くのC / C ++ヒープオブジェクトが一度に解放される単一のパスで、短時間の(通常は)多数のオブジェクトを解放できます。時間。

ウィキペディアから:

多くの実用的な目的のために、ガベージコレクションされた言語で実装された割り当て/割り当て解除集約型アルゴリズムは、手動ヒープ割り当てを使用する同等のアルゴリズムよりも実際には高速である可能性があります。これの主な理由は、ガベージコレクターにより、ランタイムシステムが割り当てと割り当て解除の操作を潜在的に有利な方法で償却できることです。

とはいえ、C#とC ++の多くを記述し、多くのベンチマークを実行しました。私の経験では、C ++はC#よりもはるかに高速です。2つの点があります。(1)C#で記述したコードをC ++に移植すると、ネイティブコードのが高速になる傾向があります。どれくらい速く?まあ、それは全体的に変化しますが、100%の速度の向上が見られることは珍しいことではありません。(2)場合によっては、ガベージコレクションによって管理対象アプリケーションの速度が大幅に低下することがあります。.NET CLRは、ヒープが大きい(たとえば、2 GBを超える)とひどい仕事をし、中間寿命のオブジェクトがほとんどない、またはまったくないアプリケーションでも、GCで多くの時間を費やすことになります。

もちろん、私が遭遇したほとんどの場合、マネージ言語は十分に高速であり、ロングショットであり、C ++の追加パフォーマンスに対するメンテナンスとコーディングのトレードオフは、単に良いものではありません。


1
問題は、Webサーバーなどの実行時間の長いプロセスの場合、メモリが時間の経過とともに(C ++で記述されたプログラムで)断片化し、ガベージコレクションに似たものを実装する必要があることです(または頻繁に再起動する、IISを参照)。 )。
Tony BenBrahim、2008

3
永久に実行することを目的とした大きなUnixプログラムでは、そのようなことは見ていません。それらはCで書かれる傾向があり、これはメモリ管理にとってC ++よりもさらに悪いです。
David Thornley、

もちろん、問題は、マネージコードとアンマネージコードのプログラムの実装を比較するのか、それとも言語の理論上の最高のパフォーマンスを比較するのかです。明らかに、管理されていないコードは、管理されたコードとまったく同じことをする管理されていないプログラムを作成することができる最悪の場合と同様に、常に少なくとも管理された速度と同じくらい高速である可能性があります。しかし、ほとんどのパフォーマンスの問題はアルゴリズムではなく、マイクロです。また、マネージコードとアンマネージコードを同じ方法で最適化しないため、 "C ++ in C#"は通常うまく機能しません。
共龍2009

2
C / C ++では、存続期間の短いオブジェクトをスタックに割り当てることができ、適切なときに割り当てます。マネージコードではできません。選択の余地はありません。また、C / C ++では、連続した領域にオブジェクトのリストを割り当てることができます(新しいFoo [100])。マネージコードではできません。そのため、比較は無効です。ええと、この選択の力は開発者に負担をかけますが、この方法で彼らは彼らが住んでいる世界を知ることを学びます(記憶......)。
Frunsi、2009

@frunsi:「C / C ++では、連続した領域にオブジェクトのリストを割り当てることができます(新しいFoo [100])。マネージコードではできません」。不正解です。ローカル値型はスタック割り当てされており、C#でそれらの配列をスタック割り当てすることもできます。C#で記述された実稼働システムでさえ、定常状態では完全に割り当て不要です。
JD


1

実際、SunのHotSpot JVMは「混合モード」の実行を使用しています。メソッドのバイトコードは、特定のコードブロック(メソッド、ループ、try-catchブロックなど)が頻繁に実行されることが(通常は何らかのカウンターを介して)決定されるまで解釈され、その後JITでコンパイルされます。メソッドをJITコンパイルするのに必要な時間は、メソッドがめったに実行されないメソッドであると解釈された場合よりも、多くの場合長くかかります。「混合モード」の場合、JVMがコードをJITする時間を無駄にすることがないため、パフォーマンスは通常より高くなります。C#および.NETはこれを行いません。.NET JITは、多くの場合、時間を浪費するすべてのものを処理します。


1

PA-8000で動作するPA-8000のインタープリターであるHP LabsのDynamoについて読んでください。多くの場合、ネイティブで実行するよりも速くプログラムを実行します。それからそれはまったく驚くべきことではないでしょう!

「中間ステップ」とは考えないでください。プログラムの実行には、他の多くのステップが任意の言語で既に含まれています。

それはしばしば次のようになります:

  • プログラムにはホットスポットがあるため、実行する必要のあるコード本体の95%の実行が遅い場合でも、ホット5%の方が高速であれば、パフォーマンスの競争力を維持できます。

  • HLLはC / C ++のようなLLLよりもあなたの意図を知っているため、より最適化されたコードを生成できます(OCamlにはさらに多くのものがあり、実際にはさらに高速です)。

  • JITコンパイラーには、静的コンパイラーにはない多くの情報があります(今回のような実際のデータなど)。

  • JITコンパイラーは、従来のリンカーが実際に許可されていないランタイムでの最適化を行うことができます(一般的なケースがフラットになるようにブランチを並べ替える、またはライブラリー呼び出しをインライン化するなど)。

全体として、C / C ++はパフォーマンスにとってかなりお粗末な言語です。データ型についての情報はほとんどなく、データについての情報はなく、実行時の最適化を可能にする動的ランタイムもありません。


1

JavaまたはCLRがC ++よりも高速である場合、短いバーストが発生する可能性がありますが、アプリケーションの存続期間全体のパフォーマンスは低下します。その結果については、www.codeproject.com / KB / dotnet / RuntimePerformance.aspxを参照してください。



1

私の理解では、C / C ++は特定のマシンアーキテクチャで実行するネイティブコードを生成します。逆に、JavaやC#などの言語は、ネイティブアーキテクチャを抽象化する仮想マシン上で実行されます。この中間ステップのため、JavaまたはC#がC ++の速度に一致することは論理的に不可能であるように見えますが、最新のコンパイラー(「ホットスポット」)はこの速度に達するか、それを超えることさえあると言われています。

それは非論理的です。中間表現を使用しても、本質的にパフォーマンスが低下することはありません。たとえば、llvm-gccはLLVM IR(仮想無限レジスタマシン)を介してCおよびC ++をネイティブコードにコンパイルし、優れたパフォーマンスを実現します(多くの場合、GCCに勝っています)。

おそらく、これは言語の問題よりもコンパイラの問題のほうが多いですが、これらの仮想マシン言語の1つがネイティブ言語よりも優れたパフォーマンスを発揮する方法を誰でも簡単に英語で説明できますか?

ここではいくつかの例を示します。

  • JITコンパイルを備えた仮想マシンは、ランタイムコード生成(System.Reflection.Emit.NETなど)を容易にするため、生成されたコードをオンザフライでC#やF#などの言語でコンパイルできますが、CまたはC ++で比較的遅いインタープリターを作成する必要があります。たとえば、正規表現を実装します。

  • CおよびC ++は十分に高速なコードを生成しないため、仮想マシンの一部(書き込みバリアやアロケーターなど)は、多くの場合、手動でコード化されたアセンブラーで記述されます。プログラムがシステムのこれらの部分にストレスをかけると、CまたはC ++で記述できるものよりもパフォーマンスが優れていると考えられます。

  • ネイティブコードの動的リンクは、パフォーマンスを妨げ、プログラム全体の最適化を回避できるABIへの準拠を必要としますが、リンクは通常VMで遅延され、プログラム全体の最適化(.NETの具体化されたジェネリックなど)の恩恵を受けることができます。

上記のpaercebalの非常に支持された回答のいくつかの問題にも対処したいと思います(誰かが彼の回答に対する私のコメントを削除し続けるため)。

コード処理はコンパイル時に行われます...

したがって、テンプレートメタプログラミングは、プログラムがコンパイル時に利用可能である場合にのみ機能します。これは、多くの場合当てはまりません。たとえば、ランタイムコード生成ができないため、バニラC ++で競争力のある正規表現ライブラリを作成することはできません(重要な側面)。メタプログラミング)。

...型での再生はコンパイル時に行われます... JavaまたはC#で同等のものを書くのはせいぜい苦痛であり、コンパイル時に型がわかっている場合でも、実行時に常に遅くなり、解決されます。

C#では、これは参照型にのみ当てはまり、値型には当てはまりません。

JIT最適化に関係なく、メモリへの直接ポインタアクセスほど高速なものはありません...メモリ内に連続したデータがある場合、C ++ポインタ(つまり、Cポインタ... Caesarに期限を与えましょう)を介してアクセスすると、時間は速くなります。 Java / C#よりも。

ポインタはエイリアシング関連の最適化を妨げるため、SciMark2ベンチマークからのSORテストでJavaがC ++を打ち負かしているのを人々は見ています

また、.NETはリンク後に動的にリンクされたライブラリ全体でジェネリックの型特化を行うのに対し、C ++はリンクの前にテンプレートを解決する必要があるため、特殊化できないことにも注意してください。そして明らかに、ジェネリックがテンプレートよりも優れている大きな利点は、わかりやすいエラーメッセージです。


0

他の何人かが言ったことに加えて、私の理解から、.NETとJavaはメモリ割り当てが優れています。たとえば、C ++がフラグメント化できないときにメモリをコンパクト化できます(本来は、賢いガベージコレクターを使用している場合は可能です)。


または、より優れたC ++アロケータやオブジェクトのプールを使用している場合。これは、C ++の観点から見ると、魔法にはほど遠いものであり、「ヒープの割り当て」がスタックの割り当てと同じくらい速くなると結論づけることができます。
paercebal 2008

すべてを常にヒープに割り当てる場合、.NETとJavaはC / C ++よりもパフォーマンスが向上する可能性があります。しかし、C / C ++ではこれを行わないだけです。
Frunsi、2009

0

多くの速度が必要な場合、JVMはC ++実装を呼び出すだけなので、ほとんどのOS関連のものに対してJVMがどれほど優れているかよりも、ライブラリがどれほど優れているかが問題になります。ガベージコレクションはメモリを半分に削減しますが、より洗練されたSTLおよびBoost機能の一部を使用しても同じ効果がありますが、バグの可能性が何倍にもなります。

多くのクラスを持つ大規模なプロジェクトでC ++ライブラリとその高レベル機能の多くを使用している場合は、JVMを使用するよりも遅くなる可能性があります。エラーが発生しやすくなります。

ただし、C ++の利点は、自分で最適化できることです。そうしないと、compiler / jvmの動作に行き詰まります。独自のコンテナーを作成し、調整された独自のメモリ管理を記述し、SIMDを使用して、あちこちでアセンブリにドロップすると、ほとんどのC ++コンパイラーが独自に実行するよりも少なくとも2倍から4倍高速化できます。一部の操作では、16x-32x。それは同じアルゴリズムを使用しており、より優れたアルゴリズムを使用して並列化すると、劇的に増加する場合があり、通常使用される方法よりも数千倍速くなる場合があります。


0

私はそれをいくつかの異なる点から見ます。

  1. 無限の時間とリソースがある場合、マネージコードまたはアンマネージコードはより高速になりますか?明らかに、答えは、アンマネージコードは常にこの側面でマネージコードを常に結び付けることができるということです。最悪の場合のように、マネージコードソリューションをハードコーディングするだけです。
  2. ある言語のプログラムを受け取り、それを別の言語に直接翻訳すると、パフォーマンスはどの程度低下しますか?おそらくのためにたくさん、任意の二つの言語。ほとんどの言語では、さまざまな最適化が必要であり、さまざまな問題があります。マイクロパフォーマンスは、多くの場合、これらの詳細を知ることについて多くのことを行います。
  3. 時間とリソースが限られている場合、2つの言語のどちらがより良い結果を生み出しますか?これは最も興味深い質問です。マネージ言語は少し遅いコードを生成する可能性がありますが(その言語用に適切に記述されたプログラムの場合)、そのバージョンはおそらくより早く実行され、最適化により多くの時間を費やすことができます。

0

非常に短い答え:一定の予算を考えると、C ++アプリケーションよりもJavaアプリケーションのパフォーマンスが向上します(ROIの考慮事項)さらに、Javaプラットフォームには適切なプロファイラーがあり、ホットスポットをより迅速に特定するのに役立ちます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.