プログラミング言語で構築された一部のアプリケーションまたはアルゴリズム、たとえばC ++ / Rustは、同じマシンで実行されているJava / Node.jsなどで構築されたものよりも高速または高速に動作することに気付きました。これに関していくつか質問があります。
- なぜこれが起こるのですか?
- プログラミング言語の「速度」を支配するものは何ですか?
- これはメモリ管理と関係がありますか?
誰かが私のためにこれを壊してくれたら本当に感謝しています。
プログラミング言語で構築された一部のアプリケーションまたはアルゴリズム、たとえばC ++ / Rustは、同じマシンで実行されているJava / Node.jsなどで構築されたものよりも高速または高速に動作することに気付きました。これに関していくつか質問があります。
誰かが私のためにこれを壊してくれたら本当に感謝しています。
回答:
プログラミング言語の設計と実装では、パフォーマンスに影響を与える可能性のある多くの選択肢があります。いくつかだけ言及します。
最終的には、すべての言語はマシンコードを実行して実行する必要があります。C ++などの「コンパイル済み」言語は、コンパイル時に一度だけ解析、デコード、およびマシンコードへの変換が行われます。「解釈された」言語は、直接的な方法で実装された場合、実行時、すべてのステップ、毎回デコードされます。つまり、ステートメントを実行するたびに、インタプリタはそれがif-then-elseか割り当てなどかをチェックし、それに応じて行動する必要があります。これは、100回ループすると、同じコードを100回デコードして時間を無駄にすることを意味します。幸いなことに、通訳者は多くの場合、ジャストインタイムコンパイルシステムなどによってこれを最適化します。(より正確には、「コンパイルされた」または「解釈された」言語のようなものはありません。これは、言語ではなく実装のプロパティです。それでも、
異なるコンパイラ/インタープリターは異なる最適化を実行します。
言語に自動メモリ管理がある場合、その実装はガベージコレクションを実行する必要があります。これには実行時コストがかかりますが、エラーが発生しやすいタスクからプログラマを解放します。
言語はマシンに近い場合があり、エキスパートプログラマーはすべてを微最適化し、CPUからより多くのパフォーマンスを引き出すことができます。しかし、実際にこれが実際に有益であるかどうかは議論の余地があります。ほとんどのプログラマーは実際に微最適化を行わず、多くの場合、コンパイラーによって平均的なプログラマーが行うよりも優れた高レベル言語を最適化できるためです。(ただし、マシンから遠く離れていることも利点がある場合があります。たとえば、Haskellは非常に高いレベルですが、その設計の選択のおかげで、非常に軽量な緑色のスレッドを使用できます。)
静的型チェックは最適化にも役立ちます。動的に型付けされたインタープリター言語では、を計算するたびx - y
に、インタープリターはしばしば両方x,y
が数字であるかどうかをチェックし、そうでなければ例外を発生させる必要があります。コンパイル時に型が既にチェックされている場合、このチェックはスキップできます。
一部の言語では、実行時エラーが常に正しい方法で報告されます。要素が20個しかないa[100]
Javaで記述a
すると、ランタイム例外が発生します。Cでは、そのプログラムが、クラッシュメモリにいくつかのランダムなデータを上書きする、あるいは実行する可能性があることを意味し、未定義の動作を引き起こす絶対に何も他の(ISO C標準は一切の制限を提起していません)。これには実行時チェックが必要ですが、プログラマーにとってより優れたセマンティクスを提供します。
ただし、言語を評価するとき、パフォーマンスがすべてではないことに注意してください。それに夢中にならないでください。すべてをマイクロ最適化しようとしても、非効率的なアルゴリズム/データ構造が使用されていることに気付かないことはよくあるtrapです。Knuthはかつて「時期尚早の最適化がすべての悪の根源だ」と言っていました。
プログラムを正しく書くのがどれほど難しいか過小評価しないでください。多くの場合、より人間に優しいセマンティクスを持つ「遅い」言語を選択する方が良い場合があります。さらに、パフォーマンスに重要な特定の部分がある場合、それらは常に別の言語で実装できます。参考として、2016 ICFPプログラミングコンテストでは、これらは受賞者が使用した言語でした。
1 700327 Unagi Java,C++,C#,PHP,Haskell
2 268752 天羽々斬 C++, Ruby, Python, Haskell, Java, JavaScript
3 243456 Cult of the Bound Variable C++, Standard ML, Python
それらのいずれも単一の言語を使用しませんでした。
プログラミング言語の「速度」を支配するものは何ですか?
プログラミング言語の「速度」のようなものはありません。特定の環境内で実行されている特定の実行エンジンの特定の実装の特定のバージョンによって実行される特定のプログラムによって書かれた特定のプログラムの速度のみがあります。
異なる実装を使用して、同じマシンで同じ言語で記述された同じコードを実行すると、パフォーマンスが大きく異なる場合があります。または、同じ実装の異なるバージョンを使用します。たとえば、10年前のSpiderMonkeyのバージョンと今年のバージョンを使用してまったく同じマシンでまったく同じECMAScriptベンチマークを実行すると、おそらく2倍から5倍、おそらく10倍のパフォーマンスの向上が得られます。同じマシンで同じプログラムを実行すると、新しい実装では2倍速くなるため、ECMAScriptはECMAScriptより2倍高速になるということですか?それは意味がありません。
これはメモリ管理と関係がありますか?
あんまり。
なぜこれが起こるのですか?
リソース。お金。Microsoftはおそらく、PHP、Ruby、Pythonのコミュニティ全体がVMで作業している人よりも、コンパイラプログラマのためにコーヒーを作る人を多く採用しています。
何らかの形でパフォーマンスに影響を与えるプログラミング言語の多かれ少なかれ機能については、解決策もあります。たとえば、C(ここではCをCの前に存在する類似言語のクラスの代用として使用しています)はメモリセーフではないため、同時に実行される複数のCプログラムが踏みにじることができますお互いの記憶。そのため、仮想メモリを発明し、すべてのCプログラムがマシン上で実行されている唯一のふりをすることができるように、すべてのCプログラムに間接層を通過させます。ただし、それは遅いため、MMUを発明し、ハードウェアに仮想メモリを実装して高速化します。
しかし!メモリセーフな言語はすべてを必要としません!仮想メモリを持つことは、彼らを少しも助けません。実際、それはさらに悪いことです。仮想メモリは、メモリセーフな言語を助けないだけでなく、仮想メモリは、ハードウェアに実装されていても、パフォーマンスに影響を与えます。これは、ガベージコレクターのパフォーマンスにとって特に有害です(これは、メモリセーフ言語の実装のかなりの数が使用するものです)。
別の例:現代の主流の汎用CPUは、洗練されたトリックを使用してキャッシュミスの頻度を減らします。これらのトリックの多くは、どのコードが実行され、将来どのメモリが必要になるかを予測しようとすることになります。ただし、実行時の多型性が高い言語(OO言語など)の場合、これらのアクセスパターンを予測するのは非常に困難です。
ただし、別の方法があります。キャッシュミスの総コストは、キャッシュミスの数に個々のキャッシュミスのコストを掛けたものです。メインストリームCPUはミスの数を減らしますが、個々のミスのコストを削減できるとしたらどうでしょうか?
Azul Vega-3 CPUは、仮想化されたJVMを実行するために特別に設計されており、ガベージコレクションとエスケープ検出(静的エスケープ解析と同等の動的)と強力なメモリコントローラーを支援するための特別な命令を備えた非常に強力なMMUとシステム全体を備えていました飛行中に20000以上の未処理のキャッシュミスが発生する可能性があります。残念ながら、ほとんどの言語固有のCPUと同様に、その設計は「巨人」のIntel、AMD、IBMなどによって単純に使い果たされ、強引に強制されました。
CPUアーキテクチャは、言語の高性能な実装がいかに簡単であるか、またはどれだけ難しいかに影響を与える1つの例にすぎません。C、C ++、D、Rustなどの言語は、Java、ECMAScript、Python、Rubyのような、CPUと「戦い」、回避する必要がある言語よりも、現代の主流のCPUプログラミングモデルに適しています。 、PHP。
本当に、それはすべてお金の問題です。ECMAScript、ECMAScript用に設計された高性能オペレーティングシステム、ECMAScript用に設計された高性能CPU、ECMAScript用に設計された高性能CPU Cに似た言語を高速化するのに何十年もかかると、おそらく同等のパフォーマンスが得られるでしょう。現時点では、ECMAScriptに似た言語を高速化するよりもCに似た言語を高速化することに多くのお金が費やされており、Cに似た言語の仮定は、MMUやCPUからオペレーティングシステム、そしてライブラリおよびフレームワークまでの仮想メモリシステム。
個人的には、Ruby(一般に「遅い言語」と考えられています)に最も精通しているためHash
、Rubinius のクラス(Rubyの中心的なデータ構造の1つ、キー値辞書)の2つの例を挙げます。Rubyの実装は100%純粋なRubyで記述されており、Hash
Cで記述されたYARV(最も広く使用されている実装)のクラス。また、YARVのC拡張として記述された画像操作ライブラリがあります。非常に動的で反射的なRubyトリックを大量に使用するCをサポートしません。Oracle LabsのTruffle ASTインタープリターフレームワークとGraal JITコンパイルフレームワークを利用するJRubyの実験ブランチは、YARVがオリジナルの高度に最適化されたCバージョンを実行できるのと同じ速さで、純粋なRuby「フォールバックバージョン」を実行できます。これは、動的な実行時間の最適化、JITコンパイル、および部分評価を使用して非常に巧妙なことを行う非常に賢い人によって簡単に(まあ、何でも)達成されます。
int
Pythonは、Pythonで使用されるような無制限の整数が数学的にはるかに自然であるにもかかわらず、パフォーマンス上の理由から固定サイズsを選択しました。ハードウェアで無制限の整数を実装することは、固定サイズの整数ほど高速ではありません。実装の詳細を隠そうとする言語は、素朴なC実装に近づくために複雑な最適化を必要とします。
理論的には、2つの言語でまったく同じコードを記述し、「完璧な」コンパイラを使用して両方をコンパイルする場合、両方のパフォーマンスは同じである必要があります。
実際には、パフォーマンスが異なる理由はいくつかあります。
一部の言語は最適化が困難です。
これには、コードをより動的にする機能(動的型付け、仮想メソッド、関数ポインター)だけでなく、ガベージコレクションなども含まれます。
このような機能を使用してコードを高速化するにはさまざまな方法がありますが、通常はそれらを使用しない場合よりも少なくともやや遅くなります。
一部の言語実装では、実行時にコンパイルが必要です。
これは特に、仮想マシンを備えた言語(Javaなど)およびソースコードを実行する言語に適用され、バイナリの中間ステップ(JavaScriptなど)はありません。
このような言語の実装は、実行時により多くの作業を行う必要があり、これはパフォーマンス、特に起動直後の起動時間/パフォーマンスに影響します。
言語の実装は、意図的に最適化に費やす時間を意図的に減らしています。
すべてのコンパイラーは、生成されたコードだけでなく、コンパイラー自体のパフォーマンスを重視します。これは、コンパイルに時間がかかりすぎるとアプリケーションの実行が遅くなるランタイムコンパイラ(JITコンパイラ)にとって特に重要です。ただし、C ++のような先行処理コンパイラにも適用されます。たとえば、レジスタ割り当てはNP完全問題であるため、完全に解決することは現実的ではなく、代わりに妥当な時間で実行されるヒューリスティックが使用されます。
異なる言語でのイディオムの違い。
共通ライブラリを使用して特定の言語に共通のスタイル(イディオマティックコード)で記述されたコードは、そのようなイディオムコードが各言語で微妙に異なる動作をするため、パフォーマンスに違いが生じる可能性があります。
例として、vector[i]
C ++、list[i]
C#、およびlist.get(i)
Javaを検討してください。C ++コードはおそらく範囲チェックを行わず、仮想呼び出しも行いません。C#コードは範囲チェックを行いますが、仮想呼び出しはなく、Javaコードは範囲チェックを行います。これは仮想呼び出しです。3つの言語はすべて仮想メソッドと非仮想メソッドをサポートし、C ++とC#の両方で範囲チェックを含めるか、基になる配列にアクセスする際に範囲チェックを回避できます。しかし、これらの言語の標準ライブラリは、これらの同等の関数を別々に記述することを決定し、その結果、それらのパフォーマンスは異なります。
一部のコンパイラでは、いくつかの最適化が欠落している場合があります。
コンパイラライターは有限のリソースを持っているため、他の問題を無視しても、可能な限りの最適化を実装することはできません。そして、彼らが持っているリソースは、最適化のある領域に他の領域よりも集中しているかもしれません。結果として、異なる言語で記述されたコードは、ある言語が他の言語よりも高速であるか、最適化が容易であるという根本的な理由がなくても、コンパイラの違いによりパフォーマンスが異なる場合があります。
これは非常に一般的な質問ですが、あなたの場合、答えは簡単かもしれません。C ++はマシンコードにコンパイルします。JavaはJavaバイトコードにコンパイルされ、Java仮想マシンで実行されます(ただし、ジャストインタイムコンパイルもあり、Javaバイトコードをネイティブマシンコードにさらにコンパイルします)。もう1つの違いは、Javaのみが提供するサービスであるガベージコレクションです。
あなたの質問は一般的すぎるので、あなたが必要とする正確な答えを出すことはできないと思います。
私の最良の説明のために、C ++および.Netプラットフォームを見てみましょう。
C ++。マシンコードに非常に近く、パフォーマンスのために、以前のプログラミング(10年以上前など)で好まれていました。IDEを使用してC ++プログラムを開発および実行するために必要なリソースはあまりなく、非常に軽量で非常に高速であると考えられています。コンソールでC ++コードを記述し、そこからゲームを開発することもできます。メモリとリソースの観点から、コンピューターでどれくらいの容量が必要かをおおまかに忘れましたが、その容量は現在のプログラミング言語の世代と比較することはできません。
では、.Netを見てみましょう。.Net開発を行うための前提条件は、1種類のプログラミング言語だけで構成されていない1つの巨大なIDEです。たとえばC#で開発したい場合でも、IDE自体にはデフォルトでJ#、VB、モバイルなどの多くのプログラミングプラットフォームが含まれます。IDEインストールで十分な経験がある場合は、カスタムインストールを実行し、必要なものだけをインストールする場合を除きます。
IDEソフトウェア自体をインストールする以外に、.Netには、開発者が必要とする、または必要としないあらゆる種類のプラットフォームへのアクセスを容易にする目的で、膨大な容量のライブラリとフレームワークが付属しています。
多くの一般的な機能とコンポーネントがデフォルトで利用可能であるため、.Netでの開発は楽しい経験になります。IDEでは、多くの検証方法、ファイル読み取り、データベースアクセスなどをドラッグアンドドロップして使用できます。
その結果、コンピューターリソースと開発速度のトレードオフになります。これらのライブラリとフレームワークは、メモリとリソースを消費します。.Net IDEでプログラムを実行すると、ライブラリ、コンポーネント、およびすべてのファイルをコンパイルしようとすると膨大な時間がかかる場合があります。また、デバッグを行う場合、IDEでデバッグプロセスを管理するには、コンピューターに多くのリソースが必要です。
通常、.Net開発を行うには、Ramとプロセッサを備えた適切なコンピューターが必要です。それ以外の場合は、まったくプログラミングしないでください。この点では、C ++プラットフォームは.Netよりもはるかに優れています。それでも優れたコンピューターが必要ですが、.Netと比較して容量はそれほど気になりません。
私の答えがあなたの質問に役立つことを願っています。もっと知りたい場合はお知らせください。
編集:
私の主なポイントを明確にするために、私は主に「プログラミングの速度を左右するもの」という質問に答えます。
IDEの観点では、相対IDEでC ++または.Net言語を使用してもコードの記述速度には影響しませんが、Visual Studioコンパイラはプログラムの実行に時間がかかるため、開発速度に影響しますが、C ++ IDEははるかに軽量ですより少ないコンピューターリソースを消費します。したがって、長期的には、ライブラリとフレームワークに大きく依存している.Net IDEと比較して、C ++タイプのIDEでプログラミングを高速に行うことができます。IDEの起動、コンパイル、プログラムの実行などを待機する時間をとると、プログラミングの速度に影響します。「プログラミングの速度」が実際に「コードを書く速度」にのみ焦点を合わせているのでない限り。
Visual Studioのライブラリとフレームワークの量もコンピューターの容量を消費します。これが「メモリ管理」の質問に答えるかどうかはわかりませんが、Visual Studio IDEを実行すると大量のリソースが使用されるため、全体的な「プログラミングの速度」が遅くなることを指摘したいと思います。もちろん、これは「コードの記述速度」とは関係ありません。
ご想像のとおり、C ++の例として使用しているだけなので、あまり多くのC ++を知りません。私の主なポイントは、コンピューターリソースを消費するVisual Studioタイプの重いIDEについてです。
アイデアを得て、スレッドスターターの質問にまったく答えなかった場合は、長い投稿をおaびします。しかし、スレッドスターターに質問を明確にし、「速く」と「遅く」について何を知っておく必要があるかを正確に尋ねるようアドバイスします。