JIT 対静的コンパイラー
以前の投稿で既に述べたように、JIT は実行時に IL/バイトコードをネイティブ コードにコンパイルできます。そのコストは言及されましたが、結論にはなりませんでした。
JIT には、すべてをコンパイルできないという大きな問題があります。JIT コンパイルには時間がかかるため、JIT はコードの一部のみをコンパイルしますが、静的コンパイラは完全なネイティブ バイナリを生成します。コンパイラは、JIT よりも簡単に優れたパフォーマンスを発揮します。
もちろん、C# (または Java、または VB) は通常、実行可能で堅牢なソリューションを生成するのに C++ よりも高速です (C++ には複雑なセマンティクスがあり、C++ 標準ライブラリは興味深く強力ですが、完全なライブラリと比較すると非常に貧弱です)。通常、C++ と .NET または Java JIT の違いはほとんどのユーザーにはわかりません。重要なバイナリについては、C++ 処理を呼び出すことができます。 C# または Java から (この種のネイティブ呼び出し自体が非常にコストがかかる場合でも)...
C++ メタプログラミング
通常、C++ ランタイム コードを C# または Java の同等のコードと比較していることに注意してください。しかし、C++ には、すぐに Java/C# を凌駕できる機能が 1 つあります。それは、テンプレート メタプログラミングです。コード処理はコンパイル時に行われ (したがって、コンパイル時間が大幅に増加します)、実行時間がゼロ (またはほぼゼロ) になります。
私はまだこれに対する実際の影響を見てきました (私は概念だけで遊んでいましたが、その時までに、違いは JIT の実行の秒数であり、C++ の実行時間はゼロでした)、これは言及する価値があります。些細な...
編集 2011-06-10: C++ では、型の操作はコンパイル時に行われます。つまり、非ジェネリック コードを呼び出すジェネリック コードを生成します (たとえば、文字列から型 T へのジェネリック パーサー、認識した型 T の標準ライブラリ API を呼び出します。パーサーをユーザーが簡単に拡張できるようにすること) は非常に簡単で非常に効率的ですが、Java や C# で同等のものを記述するのはせいぜい面倒であり、コンパイル時に型がわかっている場合でも常に遅くなり、実行時に解決されます。あなたの唯一の希望は、JITがすべてをインライン化することです。
...
編集 2011-09-20: Blitz++ の背後にあるチーム (ホームページ、ウィキペディア) はその方向に進みました。明らかに、彼らの目標は、C++ テンプレート メタプログラミングを介して、ランタイム実行からコンパイル時間まで可能な限り移動することにより、科学計算で FORTRAN のパフォーマンスに到達することです。 . したがって、私が上に書いた「これに対する実際の影響はまだ見ていない」という部分は、明らかに実際に存在します。
ネイティブ C++ のメモリ使用量
C++ には Java/C# とは異なるメモリ使用量があるため、さまざまな利点/欠点があります。
JIT の最適化に関係なく、メモリーへの直接ポインター・アクセスほど高速なものはありません (プロセッサー・キャッシュなどはしばらく無視しましょう)。したがって、メモリ内に連続したデータがある場合、C++ ポインター (つまり、C ポインター... Caesar にその理由を説明しましょう) を介してアクセスすると、Java/C# よりも数倍速くなります。また、C++ には RAII があり、C# や Java よりも多くの処理がはるかに簡単になります。using
C++ は、そのオブジェクトの存在をスコープする必要はありません。finally
また、C++ には節がありません。これはエラーではありません。
:-)
また、C# のプリミティブに似た構造体にもかかわらず、C++ の「スタック上の」オブジェクトは、割り当てと破棄にコストがかからず、クリーニングを行うために独立したスレッドで動作する GC を必要としません。
メモリの断片化に関しては、2008 年のメモリ アロケータは、通常 GC と比較される 1980 年の古いメモリ アロケータではありません。断片化が起こらないときの最適化?適切なタスクに適切なアロケーターを使用することは、C++ 開発者ツールキットの一部である必要があります。さて、アロケーターを書くのは簡単ではありません。そして、私たちのほとんどはもっとやるべきことがあります。ほとんどの場合、RAII または GC で十分です。
編集 2011-10-04:効率的なアロケーターの例: Windows プラットフォームでは、Vista 以降、低断片化ヒープがデフォルトで有効になっています。以前のバージョンでは、WinAPI 関数HeapSetInformationを呼び出すことで LFH をアクティブ化できます)。他の OS では、代替のアロケータが提供されています (https://secure.wikimedia.org/wikipedia/en/wiki/Mallocリスト)
現在、メモリ モデルは、マルチコアおよびマルチスレッド テクノロジの台頭により、やや複雑になっています。この分野では、.NET が優位に立っていると思います。Java が優位に立っているとのことでした。「ベア メタル上」のハッカーが、自分の「マシンに近い」コードを称賛するのは簡単です。しかし今では、コンパイラーに仕事を任せるよりも、手作業でより良いアセンブリーを作成する方がはるかに困難です。C++ の場合、コンパイラは通常、10 年間でハッカーよりも優れています。C# と Java の場合、これはさらに簡単です。
それでも、新しい標準 C++0x は C++ コンパイラに単純なメモリ モデルを課します。これにより、C++ の効果的なマルチプロセッシング/並列/スレッド コードが標準化され (したがって簡素化され)、コンパイラにとって最適化がより簡単かつ安全になります。しかし、その約束が守られるかどうかは、数年後にわかります。
C++/CLI と C#/VB.NET の比較
注: このセクションでは、ネイティブ C++ ではなく、.NET によってホストされる C++ である C++/CLI について説明します。
先週、.NET の最適化に関するトレーニングを受けましたが、とにかく静的コンパイラが非常に重要であることを発見しました。JITと同じくらい重要です。
C++/CLI (またはその祖先である Managed C++) でコンパイルされたまったく同じコードは、C# (またはコンパイラが C# と同じ IL を生成する VB.NET) で生成された同じコードよりも数倍高速になる可能性があります。
C++ 静的コンパイラは、C# よりも最適化済みのコードを生成するのにはるかに優れていたためです。
たとえば、.NET での関数のインライン化は、バイトコードの長さが 32 バイト以下の関数に限定されます。そのため、C# の一部のコードは 40 バイトのアクセサーを生成しますが、これは JIT によってインライン化されることはありません。C++/CLI の同じコードは、JIT によってインライン化される 20 バイトのアクセサーを生成します。
もう 1 つの例は一時変数です。一時変数は、C# コンパイラによって生成された IL で引き続き言及されている一方で、C++ コンパイラによって単純にコンパイルされます。C++ 静的コンパイルの最適化により、コードが少なくなるため、より積極的な JIT 最適化が再度承認されます。
この理由は、C++/CLI コンパイラが C++ ネイティブ コンパイラの膨大な最適化技術から利益を得ているという事実であると推測されました。
結論
私はC++が大好きです。
しかし、私が見る限り、C# または Java のほうが全体的に優れています。C++ よりも高速だからではなく、それらの品質を合計すると、C++ よりも生産性が向上し、必要なトレーニングが少なくなり、標準ライブラリがより完全になるためです。そして、ほとんどのプログラムに関しては、それらの速度の違いは (何らかの形で) ごくわずかです...
編集 (2011-06-06)
C#/.NET での私の経験
私は現在、ほぼ独占的なプロの C# コーディングを 5 か月間行っています (私の履歴書は、すでに C++ と Java でいっぱいで、C++/CLI のタッチも少しあります)。
私は WinForms (エヘム...) と WCF (クール!)、WPF (クール!!!! XAML と生の C# の両方を使用します。WPF はとても簡単で、Swing は比較できないと思います)、および C# 4.0 で遊んでいました。
結論は、C++ よりも C#/Java で動作するコードを生成する方が簡単で高速ですが、C# で強力で安全で堅牢なコードを生成するのは C++ よりもはるかに難しい (Java ではさらに難しい) ということです。理由はたくさんありますが、次のように要約できます。
- ジェネリックはテンプレートほど強力ではありません(問題を理解するために、効率的なジェネリック Parse メソッド (string から T まで) を作成するか、C# で boost::lexical_cast に相当する効率的なメソッドを作成してみてください) 。
- RAIIは比類のないままです(GCは依然としてリークする可能性があり(はい、その問題を処理する必要がありました)、メモリのみを処理します。
using
正しいDispose実装を書くのは難しいため、C#でさえ簡単で強力ではありません)
- C#
readonly
と Javafinal
は、C++ ほど有用ではありませんconst
( C# で読み取り専用の複雑なデータ (たとえば、ノードのツリー) を公開するには、C++ の組み込み機能ですが、膨大な作業が必要です。不変データは興味深いソリューションです)。 、しかし、すべてを不変にできるわけではないので、それだけでは十分ではありません)。
したがって、C# は、機能するものが必要な限り快適な言語であり続けますが、常に安全に機能するものが必要な場合はイライラする言語です。
Java は C# と同じ問題を抱えているため、さらにイライラさせられます。C# のusing
キーワードに相当するものがないため、私の非常に熟練した同僚は、リソースが正しく解放されていることを確認するのに多くの時間を費やしました。簡単でした(デストラクタとスマートポインタを使用)。
したがって、C#/Java による生産性の向上は、ほとんどのコードで目に見えるものだと思います...コードを可能な限り完璧にする必要がある日まで。その日、あなたは痛みを知るでしょう。(私たちのサーバーや GUI アプリから何を求められているのか信じられないでしょう...)。
サーバーサイド Java と C++ について
建物の反対側にいるサーバー チーム (GUI チームに戻る前に 2 年間働きました) と連絡を取り合い、興味深いことを学びました。
Java には多くのフレームワーク/ツールがあり、保守やデプロイなどが容易であるため、Java サーバー アプリが古い C++ サーバー アプリに置き換わる運命にあります。
...ここ数か月、低レイテンシの問題が頭をよぎるまでは。次に、Java サーバー アプリは、熟練した Java チームが最適化を試みたにもかかわらず、実際には最適化されていない古い C++ サーバーとの競争に単純かつ明確に負けました。
現時点では、Java サーバーを一般的な使用のために維持することが決定されていますが、パフォーマンスは依然として重要であり、低レイテンシーのターゲットには関係なく、低レイテンシーおよび超低レイテンシーのニーズに合わせて既に高速な C++ サーバー アプリケーションを積極的に最適化しています。
結論
期待ほど単純なものはありません。
Java、さらには C# は優れた言語であり、豊富な標準ライブラリとフレームワークを備えているため、すばやくコーディングしてすぐに結果を出すことができます。
しかし、生の力、強力で体系的な最適化、強力なコンパイラ サポート、強力な言語機能、および絶対的な安全性が必要な場合、Java と C# では、競合他社よりも優れた状態を維持するために必要な最後の欠落している重要な品質パーセントを獲得することは困難です。
平均的な品質のコードを生成するには、C++ よりも C#/Java の方が時間と経験の少ない開発者が必要であったかのようですが、一方で、優れた品質から完全な品質のコードが必要になった瞬間に、結果を得るのが突然簡単かつ迅速になりました。 C++で。
もちろん、これは私自身の認識であり、おそらく私たちの特定のニーズに限定されています.
しかし、それでも、GUI チームとサーバー側チームの両方で、今日起こっていることです。
もちろん、何か新しいことが起こったら、この投稿を更新します。
編集 (2011-06-22)
「パフォーマンスに関しては、C++ が大差をつけて勝っていることがわかりました。ただし、最も広範なチューニング作業も必要でした。その多くは、平均的なプログラマーが利用できない洗練されたレベルで行われました。
[...] Java バージョンはおそらく実装が最も簡単でしたが、パフォーマンスの分析が最も困難でした。特に、ガベージ コレクションに関する効果は複雑で、調整が非常に困難でした。」
ソース:
編集 (2011-09-20)
「Facebook では、『合理的に書かれた C++ コードは高速に実行される』というのが通説であり、PHP と Java コードの最適化に多大な労力が費やされていることを強調しています。逆説的に、C++ コードは他の言語よりも書くのが難しいですが、効率的なコードは[他の言語よりも C++ で書くほうが] はるかに簡単です。」
– Herb Sutterの//build/でのAndrei Alexandrescuの引用
ソース: