一部のプログラミング言語が他のプログラミング言語よりも「高速」または「低速」なのはなぜですか?


81

プログラミング言語で構築された一部のアプリケーションまたはアルゴリズム、たとえばC ++ / Rustは、同じマシンで実行されているJava / Node.jsなどで構築されたものよりも高速または高速に動作することに気付きました。これに関していくつか質問があります。

  1. なぜこれが起こるのですか?
  2. プログラミング言語の「速度」を支配するものは何ですか?
  3. これはメモリ管理と関係がありますか?

誰かが私のためにこれを壊してくれたら本当に感謝しています。


18
特にJVMとCLR については、VMの最適化に関する広範な研究が行われており、多くの状況でコンパイル済みのC ++を上回ることができますが、単純なベンチマークの誤りは簡単です。
クリリス


26
ただし、JavaとJavascriptに関連するものを一緒に束ねることは辱的です。
ラファエル

5
私はこのように広すぎる(教科書は、関連する話題について書くことができます!)または閉じ間で引き裂かれてい複製を。コミュニティ投票してください!
ラファエル

回答:


96

プログラミング言語の設計と実装では、パフォーマンスに影響を与える可能性のある多くの選択肢があります。いくつかだけ言及します。

最終的には、すべての言語はマシンコードを実行して実行する必要があります。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

それらのいずれも単一の言語を使用しませんでした。


81
Knuthからの引用全体の 1つのバージョン:「プログラマーは、プログラムの重要でない部分の速度を考える、または心配するのに膨大な時間を浪費します。これらの効率化の試みは、デバッグと保守を考慮すると、実際に強い悪影響を及ぼします。私たちは小さな効率を忘れて、時間の約97%を言うべきです。時期尚早の最適化はすべての悪の根源です。しかし、その重要な3%で機会を逃してはなりません。
デレクエルキンズ

6
また、一部の言語は、コンパイラの最適化機能に影響を与える可能性のある一見無害なものを保証します。C++とFORTRANの厳密なエイリアスを参照してください
PlasmaHH

9
@DerekElkinsによるコメントに賛成できるように参加しました。多くの場合、その引用のコンテキストが欠落しており、時にはすべての最適化が悪であると主張するという結論に至ることさえあります。
パイプ

9
@DerekElkins皮肉なことに、あなたはおそらく最も重要な部分を見落としていました:直後の2つの文。「優れたプログラマーは、このような推論によって満足に落ち着くことはありません。重要なコードを注意深く見るのが賢明でしょう。しかし、そのコードが特定された後にのみです。多くの場合、測定ツールを使用してきたプログラマーの普遍的な経験は、直感的な推測が失敗することであったため、プログラムは非常に重要です。」PDF p268
8bittree

9
@DerekElkins明確にするために、あなたがこれを主張していると言って言葉を口に入れたくはありませんが、あまりにも頻繁にベースクオートに少しだけ追加し、Knuthが時期尚早の最適化を提唱していると思う人に出くわします3記事全体が明らかになったとき、彼は常に時期尚早の最適化に反対し、ほとんどの場合、小さな最適化に反対しますが、クリティカルとして測定されたセクションでは小さな最適化を提唱します。
8bittree

19

プログラミング言語の「速度」を支配するものは何ですか?

プログラミング言語の「速度」のようなものはありません。特定の環境内で実行されている特定の実行エンジンの特定の実装の特定のバージョンによって実行される特定のプログラムによって書かれた特定のプログラムの速度のみがあります。

異なる実装を使用して、同じマシンで同じ言語で記述された同じコードを実行すると、パフォーマンスが大きく異なる場合があります。または、同じ実装の異なるバージョンを使用します。たとえば、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で記述されており、HashCで記述されたYARV(最も広く使用されている実装)のクラス。また、YARVのC拡張として記述された画像操作ライブラリがあります。非常に動的で反射的なRubyトリックを大量に使用するCをサポートしません。Oracle LabsのTruffle ASTインタープリターフレームワークとGraal JITコンパイルフレームワークを利用するJRubyの実験ブランチは、YARVがオリジナルの高度に最適化されたCバージョンを実行できるのと同じ速さで、純粋なRuby「フォールバックバージョン」を実行できます。これは、動的な実行時間の最適化、JITコンパイル、および部分評価を使用して非常に巧妙なことを行う非常に賢い人によって簡単に(まあ、何でも)達成されます。


4
技術的には言語は本質的に高速ではありませんが、一部の言語はプログラマーが高速コードを作成できるようにすることに重点を置いています。Cは、逆方向ではなく、主にCPUに最適化されています。たとえば、intPythonは、Pythonで使用されるような無制限の整数が数学的にはるかに自然であるにもかかわらず、パフォーマンス上の理由から固定サイズsを選択しました。ハードウェアで無制限の整数を実装することは、固定サイズの整数ほど高速ではありません。実装の詳細を隠そうとする言語は、素朴なC実装に近づくために複雑な最適化を必要とします。
-gmatht

1
Cには歴史があります。アセンブリ言語で記述されたシステムを他のシステムに移植可能にするために作成されました。そのため、元々の目的は、Unix用の「ポータブルアセンブラ」を提供することであり、非常に適切に設計されていました。1980年から1995年のように非常にうまく機能していたため、Windows 95が登場したときは非常に大きな量でした。
トールビョーンラヴンアンデルセン

1
リソースに関するコメントには同意しません。重要なのはチームの人数ではなく、チームの最高の人のスキルレベルです。
マイケルケイ

「おそらく、Microsoftは、PHP、Ruby、Pythonのコミュニティ全体がVMで作業している人よりも、コンパイラプログラマのためにコーヒーを作る人を多く採用している」と考えています。それにどれだけ含めるか(Microsoftは多くのコンパイラを開発しています)。たとえば、VS C ++ コンパイラチームだけは比較的小規模です。
キュービック

7

理論的には、2つの言語でまったく同じコードを記述し、「完璧な」コンパイラを使用して両方をコンパイルする場合、両方のパフォーマンスは同じである必要があります。

実際には、パフォーマンスが異なる理由はいくつかあります。

  1. 一部の言語は最適化が困難です。

    これには、コードをより動的にする機能(動的型付け、仮想メソッド、関数ポインター)だけでなく、ガベージコレクションなども含まれます。

    このような機能を使用してコードを高速化するにはさまざまな方法がありますが、通常はそれらを使用しない場合よりも少なくともやや遅くなります。

  2. 一部の言語実装では、実行時にコンパイルが必要です。

    これは特に、仮想マシンを備えた言語(Javaなど)およびソースコードを実行する言語に適用され、バイナリの中間ステップ(JavaScriptなど)はありません。

    このような言語の実装は、実行時により多くの作業を行う必要があり、これはパフォーマンス、特に起動直後の起動時間/パフォーマンスに影響します。

  3. 言語の実装は、意図的に最適化に費やす時間を意図的に減らしています。

    すべてのコンパイラーは、生成されたコードだけでなく、コンパイラー自体のパフォーマンスを重視します。これは、コンパイルに時間がかかりすぎるとアプリケーションの実行が遅くなるランタイムコンパイラ(JITコンパイラ)にとって特に重要です。ただし、C ++のような先行処理コンパイラにも適用されます。たとえば、レジスタ割り当てはNP完全問題であるため、完全に解決することは現実的ではなく、代わりに妥当な時間で実行されるヒューリスティックが使用されます。

  4. 異なる言語でのイディオムの違い。

    共通ライブラリを使用して特定の言語に共通のスタイル(イディオマティックコード)で記述されたコードは、そのようなイディオムコードが各言語で微妙に異なる動作をするため、パフォーマンスに違いが生じる可能性があります。

    例として、vector[i]C ++、list[i]C#、およびlist.get(i)Javaを検討してください。C ++コードはおそらく範囲チェックを行わず、仮想呼び出しも行いません。C#コードは範囲チェックを行いますが、仮想呼び出しはなく、Javaコードは範囲チェックを行います。これは仮想呼び出しです。3つの言語はすべて仮想メソッドと非仮想メソッドをサポートし、C ++とC#の両方で範囲チェックを含めるか、基になる配列にアクセスする際に範囲チェックを回避できます。しかし、これらの言語の標準ライブラリは、これらの同等の関数を別々に記述することを決定し、その結果、それらのパフォーマンスは異なります。

  5. 一部のコンパイラでは、いくつかの最適化が欠落している場合があります。

    コンパイラライターは有限のリソースを持っているため、他の問題を無視しても、可能な限りの最適化を実装することはできません。そして、彼らが持っているリソースは、最適化のある領域に他の領域よりも集中しているかもしれません。結果として、異なる言語で記述されたコードは、ある言語が他の言語よりも高速であるか、最適化が容易であるという根本的な理由がなくても、コンパイラの違いによりパフォーマンスが異なる場合があります。


一部の言語にはコンパイラありません。一部の言語で、コンパイラー(reference)を使用できません
ラファエル

3
一部の言語では、静的コンパイラーを使用できません。動的コンパイルは、静的プロパティの決定不能性の影響を受けません。
ヨルグW

言語が十分に異なる場合、「まったく同じコード」はありません。それらには、同じ出力またはユーザーが観察可能な動作を生成するコードがありますが、これは同じではありません。だからあなたの前提に同意しない。
アインポクルム

@einpoklum区別はありません。ここではおもしろくないと思ういくつかの例外(スレッド同期など)を除き、言語仕様では通常、観察可能な動作を維持する最適化を許可しています。ユーザーが観察できないが、最適化できない振る舞いをどのように考えていますか?
-svick

理論的には、インタープリター+プログラムのソースコードで構成されるEXEファイルを生成することにより、インタープリター言語を「コンパイル」できます。
dan04

1

これは非常に一般的な質問ですが、あなたの場合、答えは簡単かもしれません。C ++はマシンコードにコンパイルします。JavaはJavaバイトコードにコンパイルされ、Java仮想マシンで実行されます(ただし、ジャストインタイムコンパイルもあり、Javaバイトコードをネイティブマシンコードにさらにコンパイルします)。もう1つの違いは、Javaのみが提供するサービスであるガベージコレクションです。


2
これはあまりにも単純すぎる答えです。Javaの方が速い設定があります。
ラファエル

4
マシンコードにコンパイルされるJavaの実装と、C ++の解釈された実装もあります。とにかく「マシンコード」とは何ですか、JVMバイトコードをネイティブに実行するpicoJava CPUがあり、JPCでJPC x86インタープリターを実行している場合、x86オブジェクトコードを「マシンコード」にし、JVMバイトコードを作成しないのはなぜですか?
ヨルグWミットタグ

2
Nitpick:Javaはガベージコレクションを提供するだけでなく、C ++とJavaのみを考慮しても、一部のC ++フレームワーク/ライブラリプログラムにはガベージコレクション機能があります。
アインポクルム

ネイティブマシンコードにコンパイルすると、一般にパフォーマンスが向上します。ただし、場合によっては、高品質のインタープリターが素朴なジャストインタイムコンパイラーを打ち負かすことができます。
DepressedDaniel

@JörgWMittagトリックは、ネイティブマシンコード(ホストプロセッサが理解できるマシンコード)にコンパイルしてから、呼び出されたコードに直接ジャンプして、解釈なしで実行できるようにすることです。これは、x86チップ上のx86とpicoJava CPU上のJVMバイトコードになります。
DepressedDaniel

-5

あなたの質問は一般的すぎるので、あなたが必要とする正確な答えを出すことはできないと思います。

私の最良の説明のために、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びします。しかし、スレッドスターターに質問を明確にし、「速く」と「遅く」について何を知っておく必要があるかを正確に尋ねるようアドバイスします。


6
記録のためだけに。プレーンテキストエディタを使用して、コマンドラインからコンパイラを起動できます。IDE(私はあなたがVisual Studioを参照していると仮定します)は必要ありませんが、どんな大きなプロジェクト(Intellisense、デバッガーなど)でも確実に役立ちます。また、SharpDevelopのような軽いIDEがありますが、機能は少ないです。
MetalMikester

16
IDEがなくても.Netで確実に開発できます。しかし、IDEが言語で記述されたコードの速度にどのように関連しているかはわかりません。
svick

8
@MetalMikester:そしてもちろん、Visual StudioはWindowsでのC ++開発に最適なIDEです。
ヨルグWミットタグ

3
また、C ++を.Netコードにコンパイルすることは、単一のコンパイラスイッチです。想定されるキャズムは、Visual Studioでの1回のマウスクリックによるブリッジです。
MSalters

1
現代のC ++を「マシンコードに非常に近い」と呼ぶかどうかはまったくわかりません。オリジナルC、はい。ポータブルアセンブラとして考えられ、それに非常に近いままでした(ただし、標準ライブラリが成長し、さまざまなコンパイラが、元の言語からさらに離れた言語固有のアドオンを提供しています)。オリジナルのC ++(C With Classes)、または クラスを含むCのバリアントを純粋なCに書き換えることはそれほど難しくありません。その時点で、前の説明が当てはまります。しかし、テンプレート、ポリモーフィズム、およびその他すべての機能を備えた最新のC ++ですか?それはほとんど「マシンコードに非常に近い」ものではありません。
CVn
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.