JavaがC ++よりも高速になるのはなぜですか?


79

ベンチマークでJavaがC ++よりも優れている場合があります。もちろん、C ++のパフォーマンスが優れている場合もあります。

次のリンクを参照してください。

しかし、これはどのように可能ですか?解釈されたバイトコードは、コンパイルされた言語よりも高速である可能性があるということを、私の心を揺さぶります。

誰か説明してもらえますか?ありがとう!


2
あなたが見てみることができshootout.alioth.debian.org/u32/...のJava / C ++上で高速に実行する問題の種類を確認するために...問題のパターンではなく、これらの特定の問題...を参照してください
c0da

2
参照してください。Javaは遅いという評判を持っていなかったのはなぜ?このトピックの詳細については。
ペテルトレック

11
C ++で作成された実行可能バイナリよりも高速に実行されるJava VMを作成することは法律違反です(セクション10.101.04.2c)。
Mateen Ulhaq

1
@muntoo一体どういう意味ですか?何のセクション10.101.04.2c?
ハイランドマーク

3
@HighlandMarkあなたはサークルのメンバーではありません。あなたは、円の中に何が入るかを知らないはずです。円は絶対です-円の法則は自然の法則に優先します。あなたは円を無視したり質問したりすることができません。私は円であり、円は私です。
Mateen Ulhaq

回答:


108

まず、ほとんどのJVMにはコンパイラーが含まれているため、「解釈されたバイトコード」は実際には非常にまれです(少なくともベンチマークコードでは、実際のコードでは非常にまれではありません。通常、コードは、 )。

第二に、関与するベンチマークのかなりの数が非常に偏っているように見えます(意図または無能かどうか、私は本当に言うことはできません)。たとえば、数年前、私はあなたが投稿したリンクの1つからリンクされたソースコードのいくつかを見ました。このようなコードがありました:

  init0 = (int*)calloc(max_x,sizeof(int));
  init1 = (int*)calloc(max_x,sizeof(int));
  init2 = (int*)calloc(max_x,sizeof(int));
  for (x=0; x<max_x; x++) {
    init2[x] = 0;
    init1[x] = 0;
    init0[x] = 0;
  }

callocすでにゼロ化されているメモリを提供するため、forループをゼロ化して再び使用すると明らかに無駄になります。これに続いて(メモリが機能する場合)、メモリを他のデータで埋めることにより(ゼロ化されていることに依存しない)、ゼロ化はすべて不要でした。上記のコードを単純なものmalloc(普通の人が最初から使用していたように)に置き換えると、C ++バージョンの速度がJavaバージョンに勝るのに十分なほど向上しました(メモリが提供される場合、かなり広いマージンで)。

(別の例として)methcall最後のリンクのブログエントリで使用されているベンチマークを検討してください。名前(および物事がどのように見えるか)にもかかわらず、このC ++バージョンはメソッド呼び出しのオーバーヘッドについてはあまり測定していません。重要であることが判明したコードの部分は、トグルクラスにあります。

class Toggle {
public:
    Toggle(bool start_state) : state(start_state) { }
    virtual ~Toggle() {  }
    bool value() {
        return(state);
    }
    virtual Toggle& activate() {
        state = !state;
        return(*this);
    }
    bool state;
};

重要な部分は判明したstate = !state;。コードを変更してintaの代わりに状態をエンコードするとどうなるか考えてみてくださいbool

class Toggle {
    enum names{ bfalse = -1, btrue = 1};
    const static names values[2];
    int state;

public:
    Toggle(bool start_state) : state(values[start_state]) 
    { }
    virtual ~Toggle() {  }
    bool value() {  return state==btrue;    }

    virtual Toggle& activate() {
        state = -state;
        return(*this);
    }
};

この小さな変更により、全体的な速度が約5:1のマージンで向上します。ベンチマークはメソッド呼び出し時間を測定すること目的としていましたが、実際には、測定したほとんどの時間はintとの間で変換する時間でしたbool。オリジナルによって示された非効率性は不幸であることは確かに同意します-しかし、実際のコードではほとんど発生しないようであり、発生した場合/発生した場合に修正できる容易さを考えると、考えるのに苦労しますそれは多くの意味として。

誰かが関連するベンチマークを再実行することを決定した場合、Javaバージョンにもほぼ同等の些細な変更が加えられることを追加する必要があります(または少なくとも一度は作成されます-テストを再実行していません)最近のJVMがまだ確認しています)Javaバージョンもかなり大幅に改善されています。Javaバージョンには、次のようなNthToggle :: activate()があります。

public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
    this.state = !this.state;
    this.counter = 0;
}
return(this);
}

this.state直接操作する代わりにベース関数を呼び出すようにこれを変更すると、大幅に速度が向上します(ただし、変更されたC ++バージョンに対応するには十分ではありません)。

そのため、最終的に、解釈されたバイトコードとこれまでに見られた最悪のベンチマーク(私が見た)のいくつかについての誤った仮定になります。どちらも意味のある結果をもたらしていない。

私自身の経験では、経験豊富なプログラマーが最適化に同等の注意を払っていると、C ++はJavaよりも頻繁に勝ちますが、(少なくともこれら2つの間では)プログラマーやデザインほど大きな違いはありません。引用されているベンチマークは、彼らがベンチマークと称している言語についてよりも、著者の(能力)/(不)正直さについて多くを語っています。

[編集:上記の1か所で暗示されているが、おそらくそうあるべきだと直接述べられていないので、引用する結果は、その時点で最新のC ++およびJava実装を使用して5年前にテストしたときに得られた結果です。現在の実装でテストを再実行していません。しかし、一見すると、コードが修正されていないことが示されているため、変更されるのは、コンパイラーがコードの問題を隠蔽することだけです。

我々は、Javaの例を無視する場合は、しかし、あると解釈コードは(困難とやや変わったが)コンパイルされたコードよりも高速に実行するために実際に可能。

これが起こる通常の方法は、解釈されるコードがマシンコードよりもはるかにコンパクトであること、またはコードキャッシュよりも大きなデータキャッシュを持つCPUで実行されていることです。

このような場合、小さなインタープリター(たとえば、Forth実装の内部インタープリター)は、コードキャッシュに完全に収まる場合があり、それが解釈するプログラムは、データキャッシュに完全に収まる場合があります。キャッシュは通常、メインメモリよりも少なくとも10倍速く、多くの場合ははるかに速くなります(100倍になることは特に珍しいことではありません)。

そのため、キャッシュがメインメモリよりもN倍速く、各バイトコードを実装するのにNより少ないマシンコード命令が必要な場合、バイトコードが勝つはずです(単純化していますが、一般的な考え方はまだすべきだと思います)明らかである)。


26
+1、完全な確認。特に、「言語がプログラマやデザインと同じくらいの違いをもたらすことはめったにありません」-アルゴリズムを最適化できる問題につまずくことがよくあります。例えば、big-Oを改善すると、最高のコンパイラよりもはるかに後押しされます。
シュナーダー

1
「誰かが関連するベンチマークを再実行することにした場合...」2005年には、これらの古いタスクは破棄され、ベンチマークゲームで現在表示されているタスクに置き換えられました。誰もが、その後いくつかのプログラムを再実行したい場合のベンチマークゲームのホームページに表示される現在のタスクのための現在のプログラムを再実行してくださいshootout.alioth.debian.org
igouy

@igouy:一部の人々は、少なくとも現実との最小限の関係を与えるために必要な最小限の修正で、実行したベンチマークの結果を単純に確認/拒否したい場合があります。同時に、あなたは基本的に正しいです。問題のベンチマークは非常に悪いので、最も明らかなエラーを修正するだけではあまり役に立ちません。
ジェリーコフィン

そしてそれが、2005年に、それらが破棄され、現在ベンチマークゲームで示されているタスクに置き換えられた理由です。よくわからない人は、これらの古いプログラムを再実行してください。
-igouy

13
+1 CスタイルまたはJavaスタイルでC ++をコーディングし、Javaが優れていると述べる人々が好きではありません。免責事項:優れた言語は呼び出しませんが、他の言語に完全に適したスタイルでくだらないC ++コードを作成しても、両方の言語が比較可能になるわけではありません。
クリスチャンラウ

111

時間に制限のない専門家が手作業で行う C / C ++ は、少なくともJavaと同じかそれより高速になります。最終的に、Java自体はC / C ++で記述されているため、エンジニアリングに十分な労力をかけたい場合は、もちろんJavaのすべてを実行できます。

ただし、実際には、Javaは次の理由で非常に高速に実行されることがよくあります。

  • JITコンパイル-Javaクラスはバイトコードとして格納されますが、これは(通常)プログラムの起動時にJITコンパイラーによってネイティブコードにコンパイルされます。コンパイルされると、それは純粋なネイティブコードです。したがって、理論的には、プログラムが十分に長く実行されると(つまり、すべてのJITコンパイルが完了した後)、コンパイルされたC / C ++と同じように実行することが期待できます
  • Javaのガベージコレクションは非常に高速で効率的です。HotspotGCは、おそらく世界で最高のオールラウンドGC実装です。これは、サンや他の企業が何年もかけて専門家として努力した結果です。C / C ++でロールバックする複雑なメモリ管理システムは、ほとんど悪化します。もちろん、C / C ++でかなり高速/軽量の基本的なメモリ管理スキームを作成できますが、完全なGCシステムほど汎用性はありません。最新のシステムのほとんどは複雑なメモリ管理を必要とするため、Javaは実際の状況で大きな利点があります。
  • プラットフォームのターゲット設定の改善 -アプリケーションの起動(JITコンパイルなど)にコンパイルを遅らせることにより、Javaコンパイラは、実行中の正確なプロセッサを知っているという事実を利用できます。これにより、「最小公分母」プロセッサ命令セットをターゲットとする必要があるプリコンパイル済みのC / C ++コードでは実行できない非常に有益な最適化が可能になります。
  • 実行時統計 -JITコンパイルは実行時に行われるため、プログラムの実行中に統計を収集でき、最適化が可能になります(たとえば、特定の分岐が行われる確率がわかります)。これにより、Java JITコンパイラーは、C / C ++コンパイラー(最も可能性の高い分岐を事前に「推測」する必要があるため、多くの場合間違っている可能性がある)よりも優れたコードを生成できます。
  • 非常に優れたライブラリ -Javaランタイムには、優れたパフォーマンスを備えた非常によく記述されたライブラリが多数含まれています(特にサーバー側アプリケーション用)。多くの場合、これらは自分で書いたり、C / C ++で簡単に入手できるよりも優れています。

同時に、C / C ++にはいくつかの利点もあります。

  • 高度な最適化を行う時間の増加 -C / C ++コンパイルは1回行われるため、高度な最適化を行うように設定すると、かなりの時間を費やすことができます。Javaで同じことができなかった理論的な理由はありませんが、実際にはJavaにコードを比較的迅速にJITコンパイルさせたいため、JITコンパイラは「単純な」最適化に集中する傾向があります。
  • バイトコードでは表現できない命令 -Javaバイトコードは完全に汎用ですが、バイトコードではできない低レベルでできることがいくつかあります(チェックされていないポインター演算が良い例です!)。(ab)これらの種類のトリックを使用することにより、パフォーマンス上の利点を得ることができます
  • 「安全性」の制約が少ない-Javaは、プログラムが安全で信頼できることを保証するために、余分な作業を行います。例としては、配列の境界チェック、特定の同時実行性の保証、nullポインターのチェック、キャストの型安全性などがあります。C/ C ++でこれらを回避することで、パフォーマンスが向上します(おそらくこれは悪い考えです!)

全体:

  • JavaとC / C ++は同様の速度を実現できます
  • C / C ++はおそらく極端な状況でわずかな優位性を持っています(たとえば、AAAゲーム開発者がまだそれを好むのは驚くことではありません)
  • 実際には、上記のさまざまな要因が特定のアプリケーションでどのようにバランスを取るかによって異なります。

9
広告「C ++での最適化の時間」:これは、サーバーVMを選択したときにOracle VMが行う調整の1つです。長期的に高いパフォーマンスを実現するために、より高い起動コストを受け入れます。ただし、クライアントVMは、最適な起動時間のために調整されています。そのため、その区別 Java 内にも存在ます。
ヨアヒムザウアー

8
-1:C ++コンパイラーは、非常に最適化されたバイナリーを作成するのに、はるかに長い時間(文字通り、大きなライブラリーの場合)を要することがあります。Java JITコンパイラは、「サーバー」バージョンであってもそれほど時間をかけることができません。私は、Java JITコンパイラーがMS C ++コンパイラーのようにプログラム全体の最適化を実行できることを真剣に疑っています。
quant_dev

20
@quant_dev:確かですが、C ++の利点(高度な最適化を行うためのより多くの時間)として私の答えで私が言ったこととまったく同じではありませんか?では、なぜ-1なのでしょうか?
mikera

13
ガベージコレクションはJavaの速度の利点ではありません。あなたが何をしているのかわからないC ++プログラマなら、それは速度の利点にすぎません。チェックしているのが割り当て可能な速さだけであれば、はい、ガベージコレクターが勝ちます。ただし、メモリを手動で管理することで、プログラム全体のパフォーマンスを向上させることができます。
ビリーONeal

4
... しかし、C ++では、C ++プログラムの生の速度を維持しながら、実行時に同様のブランチ最適化を行う「JITのようなレイヤー」を常に理論的に配置できます。(理論的に。:()
Mateen Ulhaq

19

Javaランタイムバイトコードを解釈しません。むしろ、Just In Time Compilationと呼ばれるものを使用します。基本的に、プログラムが実行されると、バイトコードを受け取り、特定のCPU用に最適化されたネイティブコードに変換します。


実際には、はい。原則として、初期のJava仮想マシンではバイトコードインタープリターが使用されていましたが、十分に見れば、おそらくバイトコードを解釈するVMを見つけることができます。
Steve314

10
@ Steve314:しかし、純粋に解釈するVM C ++を上回るものではないため、この質問にはあまり関係ありません。
ヨアヒムザウアー

JITコンパイラーは、コードの特定の使用のために動的に最適化することもできますが、これは静的にコンパイルされたコードでは不可能です。
スターブルー

2
@starblue、まあ、それは静的コンパイルでいくらか可能です-プロファイルに基づく最適化を参照してください。
SKロジック

18

すべてが平等であると言うことができます:いいえ、Javaは決して速くなるべきではありません。Javaを常にC ++でゼロから実装することで、少なくとも同等のパフォーマンスを得ることができます。ただし、実際には:

  • JIT はエンドユーザーのマシンでコードをコンパイルし、実行している正確なCPUに対して最適化できるようにします。ここにはコンパイルのためのオーバーヘッドがありますが、集中的なアプリの場合は十分に成果が得られるでしょう。多くの場合、実際のプログラムは使用しているCPU用にコンパイルされていません。
  • Javaコンパイラーは、C ++コンパイラーよりも自動最適化の方が優れている可能性があります。または、そうではないかもしれませんが、現実の世界では、物事は必ずしも完全ではありません。
  • パフォーマンスの動作は、ガベージコレクションなどの他の要因によって異なる場合があります。C ++では、通常、オブジェクトを処理したらすぐにデストラクタを呼び出します。Javaでは、参照を解放するだけで、実際の破棄が遅れます。これは、パフォーマンスの点で、ここでもそこでもない違いの別の例です。もちろん、C ++でGCを実装して実行できると主張することもできますが、現実には少数の人々がやりたい、やりたい、できるということです。

余談ですが、これは80年代/ 90年代のCに関する議論を思い出させます。誰もが「Cはアセンブリと同じくらい高速になるのだろうか?」基本的には、答えは紙に書かれていませんでしたが、実際には、Cコンパイラはアセンブリプログラマの90%よりも効率的なコードを作成しました(まあ、少し成熟したら)。


2
GCに関しては、GCがオブジェクトの破壊を遅らせるだけではありません(長期的には問題ではありません)。実際、最新のGCでは、短命のオブジェクトの割り当て/割り当て解除は、C ++と比較してJavaで非常に安価です。
ペテルトレック

@PéterTörökはい、あなたは正しい、良い点です。
ダニエルB

9
@PéterTörökしかし、C ++では、短命のオブジェクトがスタックに置かれることが多く、そのため、GC処理されたヒープJavaが使用できるよりもはるかに高速です。
quant_dev

@quant_dev、別の重要なGC効果を忘れました:コンパクト化。だから私はどちらの方法が速いのかはわからないだろう。
SKロジック

3
@DonalFellows C ++でのメモリ管理について心配する必要があると思う理由は何ですか?ほとんどの場合、私はしません。Javaとは異なる単純なパターンを適用する必要がありますが、それだけです。
quant_dev

10

ただし、割り当てはメモリ管理の半分にすぎません。割り当て解除は残りの半分です。ほとんどのオブジェクトでは、ガベージコレクションの直接コストはゼロであることがわかります。これは、コピーコレクターが死んだオブジェクトにアクセスしたりコピーしたりする必要はなく、ライブオブジェクトのみをコピーする必要があるためです。そのため、割り当て後すぐにガベージになるオブジェクトは、収集サイクルにワークロードを提供しません。

...

JVMは驚くべきことに、開発者だけが知ることができると仮定していたことを理解するのに優れています。JVMがケースごとにスタック割り当てとヒープ割り当てを選択できるようにすることで、プログラマがスタックに割り当てるかヒープに割り当てるかを悩ませることなく、スタック割り当てのパフォーマンス上の利点を得ることができます。

http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html


これは全体像のごく一部にすぎませんが、それでもかなり重要です。
ヨアヒムザウアー

2
私はこれの実体が好きです:javaは初心者向けで、魔法のGCを信頼します。
モルグ。

1
@Morg:または、そのように読むこともできます。Javaは、少し手間をかけて手動でメモリ管理することに時間を浪費するのではなく、物事を成し遂げたい人向けです。
ランデイ

4
@Landeiまともな長続きする広く使われているコードベースがJavaで書かれていれば、あなたのコメントはずっと信頼できると思います。私の世界では、実際のOSはCで書かれており、postgreSQLはCで書かれています。Javaは(そしてそれが公式バージョンでもあります)スキルの低い人々が群れでプログラミングを行いながら、具体的な結果に到達することを可能にしました。
モルグ。

1
@Morg OSだけに集中しているように見えるのは非常に奇妙です。いくつかの理由から、これは単に適切な測定値ではありません。まず、OSの要件は他のほとんどのソフトウェアとは決定的に異なります。次に、Pandaの親指の原則があります(別の言語で完全なOSを書き直したい人、動作する無料の代替品さえあれば自分のOSを書きたい人はいますか?)他の3番目のソフトウェアはOSの機能を使用するため、ディスクドライバー、タスクマネージャーなどを記述する必要はありません。(OSだけに基づかない)より良い引数を提供できない場合は、嫌悪感を覚えます。
ランデイ

5

完全に最適化されたJavaプログラムが完全に最適化されたC ++プログラムに勝ることはめったにありませんが、メモリ管理などの違いにより、Javaで慣用的に実装される多くのアルゴリズムが、C ++で慣用的に実装される同じアルゴリズムよりも速くなる可能性があります。

@Jerry Coffinが指摘したように、簡単な変更でコードをはるかに高速化できる場合が多くありますが、多くの場合、パフォーマンスの改善に値するために、ある言語または他の言語であまりにも多くの不潔な調整が必要になる場合があります。これはおそらく、JavaがC ++よりも優れていることを示す優れたベンチマークで見られるものです。

また、通常はそれほど重要ではありませんが、JavaなどのJIT言語では実行できるがC ++では実行できないパフォーマンスの最適化がいくつかあります。Javaランタイムには、コードのコンパイルに改善を含めることができます。つまり、JITは最適化されたコードを生成して、新しい(または少なくとも異なる)CPU機能を利用できる可能性があります。このため、10年前のJavaバイナリは、10年前のC ++バイナリよりも潜在的に優れている可能性があります。

最後に、全体像の完全な型安全性は、非常にまれなケースですが、極端なパフォーマンスの向上をもたらします。ほぼ完全にC#ベースの言語で記述された実験的なOSであるSingularityは、ハードウェアプロセスの境界や高価なコンテキストスイッチが不要なため、プロセス間通信とマルチタスクがはるかに高速です。


5

Tim HollowayによるJavaRanchの投稿:

プリミティブな例を次に示します。機械が数学的に決定されたサイクルで動作する場合、分岐命令には通常2つの異なるタイミングがありました。1つは分岐が行われたとき、もう1つは分岐が行われなかったときです。通常、分岐のない場合の方が高速でした。明らかに、これは、どのケースがより一般的であるかという知識に基づいてロジックを最適化できることを意味します(「知っている」ことが必ずしも実際のケースとは限らないという制約があります)。

JIT再コンパイルは、これをさらに一歩進めます。実際のリアルタイムの使用状況を監視し、実際に最も一般的なケースに基づいてロジックを切り替えます。そして、ワークロードがシフトした場合は、元に戻します。静的にコンパイルされたコードではこれができません。これが、Javaが手作業で調整されたアセンブリ/ C / C ++コードよりも優れている場合がある方法です。

ソース:http : //www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time


3
また、これは間違っている/不完全です。プロファイルに基づく最適化を備えた静的コンパイラーはこれ認識できます。
コンラッドルドルフ

2
Konrad、静的コンパイラは現在のワークロードに基づいてロジックを反転できますか?私が理解しているように、静的コンパイラは一度コードを生成し、それは永遠に同じままです。
チアゴネグリ

2
現在のワークロード、いいえ。しかし、典型的なワークロード。プロファイルに基づく最適化では、HotSpot JITと同様に、プログラムが通常の負荷の下でどのように実行されるかを分析し、それに応じてホットスポットを最適化します。
コンラッドルドルフ

4

これは、C ++プログラムをビルドするときに明示的にではなく、Javaプログラムの実行時にマシンコードを生成する最終ステップがJVM 内で透過的に行われるためです。

最新のJVMは、可能な限り高速にバイトコードをネイティブマシンコードにオンザフライでコンパイルするのに非常に多くの時間を費やしているという事実を考慮する必要があります。これにより、JVMは実行中のプログラムのプロファイリングデータを知ることでさらに改善できるあらゆる種類のコンパイラトリックを実行できます。

ゲッターを自動的にインライン化するようなもので、値を取得するためにJUMP-RETURNが必要ないように、物事を高速化します。

ただし、高速プログラムを実際に許可したのは、後でクリーンアップすることです。Javaのガベージコレクションメカニズムは、Cの手動のmalloc-free より高速です。多くの最新のmalloc-free実装は、下にあるガベージコレクタを使用します。


この埋め込まれたものは、より良いコードが追いつくまでJVMの起動を大きく、遅くすることに注意してください。

1
「最近のmallocなしの実装の多くは、下にガベージコレクタを使用しています。」本当に?もっと知りたいです。参考文献はありますか?
ショーンマクミラン

ありがとうございました。JVMには、実行可能コードにコンパイルするジャストインタイムコンパイラーだけでなく、実行中のコードをプロファイルし、結果としてさらに最適化するホットスポットコンパイラーが含まれなくなったと言う方法を見つけようとしていました。私は、C ++のような1回限りのコンパイラーがそれと一致するのに苦労しています。
ハイランドマーク

@SeanMcMillan、私はmallocなしの実装のパフォーマンスに関する分析をしばらく前に見ましたが、最速のものは下のガベージコレクターを使用することが言及されました。どこで読んだか思い出せません。

BDWの保守的なGCでしたか?
デミ

4

短い答え-そうではありません。それを忘れて、トピックは火や車輪と同じくらい古いです。Javaまたは.NETは、C / C ++よりも高速ではありません。最適化についてまったく考える必要のないほとんどのタスクに十分な速度です。フォームやSQL処理に似ていますが、それで終わりです。

ベンチマーク、または無能な開発者によって作成された小さなアプリの場合、最終的な結果は、Java / .NETがおそらく近くなり、さらに高速になるでしょう。

実際には、スタック上のメモリの割り当て、または単にmemzonesを使用するなどの単純なことは、その場でJava / .NETを単純に強制終了します。

ガベージコレクションの世界では、すべてのアカウンティングでmemzoneを使用しています。memzoneをCに追加すると、Cはその場で高速になります。特に、JavaとCの「高性能コード」ベンチマークでは、次のようになります。

for(...)
{
alloc_memory//Allocating heap in a loop is verrry good, in't it?
zero_memory//Extra zeroing, we really need it in our performance code
do_stuff//something like memory[i]++
realloc//This is lovely speedup
strlen//loop through all memory, because storing string length is soo getting old
free//Java will do that outside out timing loop, but oh well, we're comparing apples to oranges here
}//loop 100000 times

C / C ++(または新しい配置)でスタックベースの変数を使用してみてください。これらはsub esp, 0xff、単一のx86命令に変換され、Javaでそれを打ち負かすことができません。

ほとんどの場合、C ++とJavaを比較するベンチを見ると、次のようになります。間違ったメモリ割り当て戦略、リザーブなしの自己成長コンテナ、複数の新しいコンテナ。これは、パフォーマンス指向のC / C ++コードに近いものでもありません。

また、よく読んでください:https : //days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf


1
違う。完全に間違っています。手動のメモリ管理を使用して、コンパクト化するGCを上回ることはできません。単純な参照カウントは、適切なmark'n'sweepよりも優れていることはありません。複雑なメモリ管理になるとすぐに、C ++は遅延になります。
SKロジック

3
@ SK-Logic:間違っています。memzonesまたはスタックの割り当てでは、メモリの割り当ても割り当て解除もまったくありません。メモリーのブロックがあり、そこに書き込むだけです。並行性保護InterlockedExchangeなどのような揮発性変数でブロックをフリーとしてマークすると、次のスレッドは、空きがあると判断した場合、メモリのOSにまったくアクセスせずに、事前に割り当てられたブロックにデータをダンプします。スタックを使用すると、スタックに50MBをダンプできないという唯一の例外を除いて、さらに簡単になります。そして、そのオブジェクトの有効期間は{}内のみです。
コーダー

2
@ SK-logic:コンパイラは最初に正しさ、次にパフォーマンスです。検索エンジン、データベースエンジン、リアルタイムトレーディングシステム、ゲームは、パフォーマンスが重要だと考えています。そして、それらのほとんどは平らな構造に依存しています。いずれにせよ、コンパイラはほとんどC / C ++で書かれています。カスタムアロケーターを使用すると思います。繰り返しますが、rammapでツリーまたはリスト要素を使用しても問題はありません。プレースメントnewを使用するだけです。それほど複雑ではありません。
コーダー

3
@ SK-logic:それほど高速ではありません。私が見たすべての.NET / Javaアプリは、常に低速で、実際に独り占めになりました。管理対象アプリをSANE C / C ++コードに書き換えるたびに、アプリはよりクリーンで軽量になりました。管理対象アプリは常に重いです。VS2010と2008を参照してください。同じデータ構造ですが、VS2010はHOGです。正しく作成されたC / C ++アプリは通常、ミリ秒で起動し、スプラッシュ画面でスタックすることはなく、メモリの消費量も大幅に少なくなります。唯一の欠点は、ハードウェアを念頭に置いてコーディングしなければならないことであり、多くの人々は、それが今日どのようになっているかを知らないということです。管理対象にチャンスがあるのはベンチマークのみです。
コーダー

2
あなたの事例証拠は数えません。適切なベンチマークは本当の違いを示しています。かさばって最適ではないGUIライブラリにバインドされているGUIアプリケーションを参照しているのは特に奇妙です。そして、より重要なこと-理論的には、適切に実装されたGCのパフォーマンス制限ははるかに高くなります。
SKロジック

2

現実には、どちらもプログラマーが指示したとおりに行う高レベルのアセンブラーであり、プログラマーが指示したとおりの順序でプログラマーが指示したとおりです。パフォーマンスの違いは非常に小さいため、すべての実用的な目的にとって重要ではありません。

言語は「遅い」わけではなく、プログラマーは遅いプログラムを書きました。研究の著者が彼の特定のgrを磨こうとしない限り、プログラムが代替言語の最良の方法を使用して同じことを行うプログラムよりも(実用的な目的のために)1つの言語で最良の方法で書かれることは非常にまれです。

明らかに、ハードリアルタイムの組み込みシステムのようなまれなエッジケースに行く場合、言語の選択が違いを生む可能性がありますが、これはどのくらいの頻度ですか?そして、それらのケースのうち、正しい選択がどれくらいの頻度で盲目的に明らかでないか。


2
理論的には、「理想的な」JITting VM 、動的に収集されたプロファイリング情報に合わせて最適化を調整することにより、静的にコンパイルされたコードよりも優れている必要あります。実際には、JITコンパイラーはまだそれほどスマートではありませんが、少なくとも、より大きく、より遅い静的ピアと同等の品質のコードを生成することができます。
SKロジック

2

次のリンクを参照してください...しかし、これはどのように可能ですか?解釈されたバイトコードは、コンパイルされた言語よりも高速である可能性があるということを、私の心を揺さぶります。

  1. それらのブログ投稿は信頼できる証拠を提供しますか?
  2. それらのブログ投稿は決定的な証拠を提供しますか?
  3. それらのブログ投稿は、「解釈されたバイトコード」についての証拠さえ提供しますか?

キース・リーは「明らかな欠陥」があると言いますが、それらの「明白な欠陥」については何もしません。2005年に、これらの古いタスクは破棄され、ベンチマークゲームで現在表示されているタスクに置き換えられました。

キースリーは、「今では時代遅れのGreat Computer Language ShootoutからC ++とJavaのベンチマークコードを取得し、テストを実行しました」と言いますが、実際には、それらの時代遅れのテストのうち25のうち14の測定のみを示しています

キース・リーは、7年前にブログ投稿で何も証明しようとしていないと言いましたが、当時、彼は「Javaが遅いと言うのを聞いてうんざりしていました。当時彼が証明しようとしていたことがありました。

クリスチャンフェルデは、「コードを作成したのではなく、テストを再実行しただけです」と言っています。キースリーが選択したタスクとプログラムの測定値を公表するという彼の決定に対する責任から彼を免除したかのように。

25個の小さなプログラムでも、確実な証拠が得られますか?

これらの測定値は、Javaが解釈されない「混合モード」Javaとして実行されるプログラムに関するものです- 「HotSpotの仕組みを思い出してください」。Javaがバイトコードのみを解釈するように強制することができるため、Javaが「解釈されたバイトコード」をどれだけうまく実行できるかを簡単に見つけることができます。


-1

「解釈されたバイトコード」というこの奇妙な概念がどれほど普及しているのか、私は楽しみにしています。JITコンパイルについて聞いたことがありますか?引数をJavaに適用することはできません。

ただし、JVMを別にすれば、直接スレッド化されたコードや、ささいなバイトコードの解釈でさえ、非常に最適化されたネイティブコードよりも簡単に優れている場合があります。説明は非常に簡単です。バイトコードは非常にコンパクトであり、同じアルゴリズムのネイティブコードバージョンで1回の反復で複数のキャッシュミスが発生すると、小さなキャッシュに収まります。


解釈の普及は、おそらくコンピューターサイエンスに精通した人々によるものです。Java仮想マシンは、Javaバイトコードを受け入れ、機能的に同等のネイティブプログラムを作成することなく、Javaバイトコードをネイティブに実行できる/ not /マシンで実行するマシンです。エルゴそれは通訳です。そのキャッシングテクニックには、JITまたはその他の任意の名前を付けることができますが、インタープリターの定義によるインタープリターです。
チトン

@thiton、あなた自身のCS-fuはちょっと弱いかもしれません。JVMはどんな種類の解釈も行いません(ホットスポットの場合)。CSに言及する勇気がある人は、それ何を意味するのか、そして解釈の操作上のセマンティクスがネイティブコードの実行とどのように異なるかを知る必要があります。しかし、おそらく、コンパイルと解釈を区別するのに十分なCSを知らないだけです。
SKロジック

2
うーん、しかし、実行するためにはバイトコードをネイティブコードに変換する必要があります-JavaバイトコードをCPUに送ることはできません。したがって、サイズ引数は無効です。
quant_dev

もちろん、@ quant_dev-このケースはJVMとはまったく関係ないと言いました。その効果を機能させるには、はるかに単純なバイトコードエンジンが必要です。
SKロジック

-1

JIT、GCなどは別として、C ++は非常に簡単にJavaよりもはるかに遅くすることができます。これはベンチマークには表示されませんが、Java開発者とC ++開発者によって作成された同じアプリは、Javaではるかに高速になる可能性があります。

  • 演算子のオーバーロード。「+」や「=」などの単純な演算子はすべて、安全性チェック、ディスク操作、ロギング、追跡、プロファイリングを行う数百行のコードを呼び出す場合があります。そして、それらは非常に使いやすいので、一度演算子をオーバーロードすると、使用法がどのように積み重なるかに気付かずに、自然かつ豊富に使用します。
  • テンプレート。これらは、メモリほどの速度には影響しません。テンプレートを慎重に使用しないと、気付かないうちに数百万行のコード(基本テンプレートの代替)が生成されます。しかし、その後、バイナリの読み込み時間、メモリ使用量、スワップ使用量-すべてがベンチマークに対しても作用します。また、RAMの使用量は屋根を通ります。

高度な継承パターンに関しては、これらは非常に似ています-C ++にはJavaにはないものがあり、その逆もありますが、それらはすべて同様の大きなオーバーヘッドをもたらします。そのため、オブジェクト中心のプログラミングでは特別なC ++の利点はありません。

もう1つ注意点があります。GCは、割り当てを手動で管理するよりも高速または低速です。多くの小さなオブジェクトを割り当てると、GC環境では通常、メモリのチャンクが割り当てられ、新しいオブジェクトの必要に応じてその一部がディスパッチされます。管理対象-各オブジェクト=個別の割り当てにはかなりの時間がかかります。OTOH、大量のメモリを一度にmalloc()してから、その一部をオブジェクトに手動で割り当てるか、オブジェクトのいくつかの大きなインスタンスを使用すると、はるかに高速になります。


4
私は両方の点に同意しません。演算子を使用するかメソッドを使用するかは関係ありません。あなたは彼らが増殖すると言います。ナンセンス-メソッドよりも多くない; それらを呼び出す必要があるかどうか。また、テンプレートを使用すると、複数回使用するために特定のコードを再度手書きするよりも多くのコードは作成されません。ランタイムディスパッチ(仮想関数)よりも多くのコードが存在する可能性がありますが、これも無関係です。命令キャッシュラインのパフォーマンスはタイトループで最も重要であり、ここではテンプレートのインスタンス化が1つだけ使用されるため、関連するメモリプレッシャーはありませんテンプレートが原因です。
コンラッドルドルフ

通常の考え方では、メソッドは高価であり、演算子は安価です。必要な場合はメソッドを使用し、時間を節約して最適化する場合はオペレーターを使用します。技術的な問題ではなく、心理的な問題です。オペレーターが「重い」ということではなく、操作がはるかに簡単で、頻繁に使用されます。そして突然、コード全体が大幅に遅くなります- (プラスあなたはそれがオリジナルと同じ、プラス余分をやらせて、既存のコードで一般的に使用される演算子をオーバーロードすることができます。
SF。

私は、この心理学的事実は真実であり、たとえそうであっても、選択肢はありません。機能が必要な場合は、それが演算子またはメソッドにカプセル化されているかどうかを使用します。心理学は、セマンティクスの選択とは無関係です。
コンラッドルドルフ

1
ひっかけ質問。私は、測定行動するだろう、すべてでこれを第二推測ではないでしょう、その後。私はその戦術で問題を抱えたことはありません
コンラッドルドルフ

1
@KonradRudolph:これは、コードの明快さと記述のしやすさの点ですべて当てはまり、バグがなく、保守しやすくなっています。ただし、アルゴリズムの実装の効率に関するポイントはまだあります:obj.fetchFromDatabase("key")同じキーに対して5行のコード内で3回書き込もうとしている場合、その値を1回フェッチしてローカル変数にキャッシュするかどうかを2度考えます。データベースフェッチとして機能するようにオーバーロードされた状態で記述obj->"key"する場合->、操作のコストが明らかではないため、単に通過させる傾向があります。
SF。

-2

どういうわけかStack Exchangeは他のスタックポイントを取得しないので、残念ながら返事はありません...

しかし、ここで2番目に多く投票された答えは、私の謙虚な意見では誤報に満ちています。

C / C ++の専門家による手巻きアプリは、常にJavaアプリケーションよりもずっと高速になります。「JavaまたはFasterほど高速」というものはありません。以下に引用する項目があるため、それはただ高速です。

JITコンパイル:自動オプティマイザーがエキスパートプログラマーの能力を備え、意図とCPUが実際に実行するコードとのリンクを確認することを本当に期待しますか?さらに、あなたがするすべてのJITは、すでにコンパイルされたプログラムと比較して時間のロスです。

ガベージコレクションは、プログラマが多かれ少なかれ効率的な方法で、割り当て解除を忘れていたリソースを単に割り当て解除するツールです。

明らかに、これは、Cプログラマーが専門家(用語を選んだ)がメモリを処理するために行うよりも遅くなる可能性があります(正しく作成されたアプリにリークはありません)。

パフォーマンスが最適化されたCアプリケーションは、実行中のCPUを認識し、そのCPUでコンパイルされています。それ以外の場合、パフォーマンスのためのすべてのステップを十分に実行しなかったことを意味しますか。

実行時の統計 これは私の知識を超えていますが、Cの専門家は、自動最適化を再び上回る十分な分岐予測知識を持っていると思われます-

非常に優れたライブラリ Javaのライブラリを介してすぐに利用できる最適化されていない関数が多数あり、どの言語でも同じことが言えますが、最適化されたライブラリは特に計算用にCで記述されています。

JVM は抽象化の層です。これは、その多くが上にある優れた点と、設計によって全体的なソリューションが遅くなることの両方を意味します。

全体:

Javaは、多くの保護、機能、ツールを備えたJVMで動作するため、C / C ++の速度に到達することはできません。

C ++は、コンピューティングまたはゲーム用の最適化されたソフトウェアに明確な優位性を持ち、最高のJava実装が2ページ目でしか見られないという点で、C ++実装がコーディングコンテストで勝つのを見るのが一般的です。

実際には、C ++はおもちゃではなく、ほとんどの現代言語で処理できる多くの間違いを回避することはできませんが、よりシンプルで安全性が低いため、本質的に高速です。

そして結論として、私はほとんどの人がこれについて2セントを与えず、最終的に最適化は非常に少数の幸運な開発者のみに予約されたスポーツであり、パフォーマンスが本当に懸念される場合を除いて言いたいと思います(ハードウェアを10倍しても役に立たない-または少なくとも数百万を表す)、ほとんどのマネージャーは最適化されていないアプリと大量のハードウェアを好みます。


再び。ガベージコレクションは、「割り当てを解除するツール」だけではありません。GCは構造をコンパクト化できます。GCは、弱参照を処理し、この方法でキャッシュのバランスを取るのに役立ちます。多段GCはヒープ割り当てが作るずっと あなたのかさばる、遅いより安いですnewmalloc()。一般に、手動のメモリ管理よりもはるかに高速です-オブジェクトを手動で再配置できないためです。したがって、あなたの推論はすべて明らかに間違っており、偏っています。GCアルゴリズムとJIT最適化方法に関する知識は限られています。
SKロジック

4
この答えは、現代のオプティマイザーができることについての誤解に満ちています。手作業で最適化されたコードは、それに反することはありません。ただし、C ++ は最適化コンパイラあります。
コンラッドルドルフ

コメントSK-logicに感謝しますが、あなたがそれを述べているように、GC 一般的にはるかに速くなる可能性があります。特定のケースで何が最速になるかについて話しているのです。プログラマーができることをしてください。もちろん、直接メモリにアクセスできる場合は、オブジェクトを手動で再配置できます。JVM内部の私の知識は確かに限られており、Javaヘッドが光を見せてくれることを期待しています。手動でできないことをGCが行うことができることについてランダムなくだらないことを教えてくれるだけではありません(GC ... CPU命令を使用します;))。
モルグ。

コンラッド、私は現代のオプティマイザーを過小評価していることに同意します...しかし、手で最適化されたコードを自動最適化されたコードよりも劣ると考えるのは面白いと思います。コンパイラーは、人間ができないことを正確にどのように期待していますか?
モルグ。

1
右 。-1を押し続けると、C ++がJavaよりも高速であるという事実は変わりません。私は現代のコンパイラについてはあまり知らないかもしれませんが、それは主要な点に何の違いももたらさないでしょう。それは正しいことであり、ここで最も投票された答えと矛盾します。HPCのGPUでnVidiaが優先するのはなぜC ++なのか。他にすべてのゲームがC ++で書かれているのはなぜですか、他のすべてのDBエンジンはCで書かれているのはなぜですか?
モルグ。

-4

少なくとも2つの印象的なmmoがJavaで行われているのを見てきました。これは、ゲームに十分な速さがないというのは間違った言い方です。ゲーム設計者が他の言語よりもC ++を好むからといって、単にJavaに関連しているだけではないというだけで、プログラマーが他のプログラミング言語/パラダイムに手を出したことがないと言うだけです。C / C ++またはJavaのような高度な言語のあらゆるものが、速度の議論に技術的に対応または打ち勝つコードを生成できます。それはプログラマーが知っていること、チームが最も協力していること、そして最も重要なことは彼らが言ったツールを使用する理由です。プログラミングのゲーム開発の側面に取り組んでいるので、議論にはもっと多くのことが必要です。簡単に言えば」s QAを満たすツールを使用することでビジネスに没頭するためのお金と時間についてのすべてであり、現実の世界では、Javaまたは他の言語よりもC ++を選択するというxxの理由は重要ではありません。それは単なる大量生産の決定です。最も基本的なレベルのコンピューティングアルゴリズムで遊んでいるのは1と0だけであり、速度の引数はゲームに適用される最も愚かな引数の1つです。ひどく速度の向上が必要な場合は、プログラミング言語を完全に削除し、アセンブリを操作してください。


2
このテキストの壁は、他の回答でまだ述べられていないものを追加するようには見えません。してください編集読みやすくするためにあなたの答えを、そしてあなたの答えは、他の答えが提起したではない点に対処することを確信してください。それ以外の場合は、この時点でノイズが追加されるだけなので、回答を削除することを検討してください。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.