私はプログラミングにかなり慣れていませんが、大学で3年間コンピューターサイエンスを学びましたが、ご存じのように、学校は本格的なプログラマーの2%にすぎません。
言語xの方が言語yより効率的であると人々が言う理由を理解するのに多くの問題があります。コンパイル済みとランタイムコンパイル済みの場合のみ理解しています。コードで定数のようなデータ型を定義することは、コンピューター/言語がそれを理解させるよりも高速であることを理解している(phpやRubyなど)が、CまたはJava whatを使用することになるとCが高速になるのはなぜですか?どちらも可能な最も効率的な方法でマシン言語にコンパイルされるのではないですか?
私には、CやJavaのような言語を使用することの唯一の違いは、Javaのようなより高いレベルの言語の方が整理しやすく、クラスと継承を使用して大規模なアプリケーションを作成/維持しますが、コンパイルすると実際には何も変わらないような気がします。
ところで、私はphp、Java、Ruby、vb、c#のようなより高いレベルの言語しか知りません。多分それが私が想像するのが難しい理由ですか?私が探求したい次の言語はおそらくCです
パフォーマンスは実際には言語ではなく実装の結果ですが、実際には、より速い言語と遅い言語があります。
通常、Cは比較で最速です。 Cコンパイラは比較的成熟しており、Cプログラムには最小限のランタイムサポートが必要です。 Cプログラムは通常、コンピューター側で少し準備するだけで、ロードして実行できるものにコンパイルされます。 (Cインタープリターがあり、予想通り遅いものでした。)
Fortranは通常これらの計算にはありませんが、ほとんどの点で似ています。 Fortranコンパイラーは、たとえば、乗算プログラムに渡される3つの行列は互いに素であり、それに基づいて最適化できるため、Fortranコンパイラーは元の標準のCよりも本質的に大規模浮動小数点計算で高速でした。 Cコンパイラはそれを想定できませんでした。
Javaプログラムは通常、人工機械言語にコンパイルされ、通常はオンザフライでコンパイルされます(ジャストインタイムコンパイル)。これは理論的にはCスタイルのコンパイルよりも高速である可能性があります(実行の流れをより正確に推測でき、使用中の正確なシステムに合わせてコンパイルを調整できます)が、実際にはそうではありません。 Javaには、ガベージコレクターなどのランタイムサポートも必要です。JITコンパイラーとランタイムはロードして実行する必要があります。その結果、起動時間が長くなり、顕著になる場合があります。
Pythonプログラムは通常、人工機械言語にコンパイルされてから解釈されますが、処理速度は遅くなります。コンパイルされたファイル( ".pyc")を保存することは可能ですが、多くの場合ソースのみが保存されるため、実行するにはまずコンパイルしてから解釈する必要があり、処理が遅くなります。また、Pythonには動的型付けがあります。つまり、コンパイラはすべての型が事前にわかっているわけではないため、Python関数は異なるデータを取得できる必要があります実行時の型。これは非効率的です。
常に驚きの余地があります。ある有名な機会に、CMU Common LISPプログラムがFortranプログラムを圧倒しました。一般的なLISPはガベージコレクションを必要としますが、これは明らかにそのアプリケーションの問題ではなく、通常は動的に型指定されますが、すべての型を静的に宣言することは可能です。 Fortranコンパイラーには、CMU Common LISPコンパイラーにはなかったわずかな非効率性があり、その後、適切に改善されました。
言語Xが言語Yよりも効率的であると判断する前に、「XはYよりも効率的」な方法を知る必要があります。
一部の言語は、実行時の実行速度がより効率的であり、一部の言語は、メモリフットプリントがより効率的であり、タイプされたテキストの行がより効率的です。気になる効率とあまり気にしない効率があります。気になる効率の高い言語をターゲットにしてください。
言語はツールのようなものであることを忘れないでください。焦点は、ツールの効率にあるのではなく、仕事に適したツールを選択することにあるべきです。ネイルガンは、家の骨組みを構築するためにハンマーよりもはるかに効率的です。ネイルガンは、単一の額縁をぶら下げるためのハンマーよりもはるかに効率的ではありません。ジョブに適したツールを選択します。
具体例:次のコードを見てみましょう:
x.Run()
これはRun
メソッドの呼び出しです。マシンコードでは、これは次のようになります。
$a0 = x // Use $a0/$a1 for argument passing
$r1 = [address of Run method]
call $r1
[address of Run method]
はトリッキーな部分です。静的に型付けされたOO JavaまたはC++のような言語では、メソッドの配列である仮想メソッドテーブル(VMT)で検索します。コンパイル時に、Run
がVMTの2番目のエントリであることを知っている可能性があるため、オフセット4(32ビットマシン上)からロードできます。
$a0 = load x
$r2 = load $a0+0 // The VMT is the first address in the object
$r1 = load $r2+4 // Load the 2nd address from the VMT
call $r1
Pythonなどの言語では、オブジェクトのメソッドテーブルは、文字列からアドレスへのルックアップデータ構造の一種で、おそらくハッシュテーブルであり、最初にlookup
関数を呼び出す必要があります。マシンコードは次のようになります。
$a0 = x
$a0 = load $a0 // Load the address of the method hashtable
$a1 = "Run"
$v0 = call lookup // Expensive operation
$a0 = x
call $v0
したがって、両方の言語で「同じこと」を実行している場合でも、C++とJavaでの静的型付けにより、コンパイラーはより小さく、より高速なマシンコードを生成できます。
[〜#〜] note [〜#〜]:スピードアップする方法があります。たとえば、Pythonコードのプログラム全体の分析を実行して、コンパイル時にRunメソッドのアドレスを特定し、特定することでハッシュテーブルのルックアップを取り除くことができます。しかし、私の例のポイントは、どちらの場合も「マシンコードにコンパイル」している場合でも、パフォーマンスの違いがどこから発生するかを示すことでした。
まず第一に、いいえ。それは問題ではありません。非常に制約された環境用のコードを記述しているわけではないため、プロファイラーで示されているように、高水準言語で記述して最適化(奇数のCモジュールの記述を含む)する必要があります。
次に、JavaはWordの通常の意味でのコンパイル済み言語ではないため、Cとの比較は不十分です。C++などの別のコンパイル済み言語との比較は、より適切です。 Ada、D、Go、またはOCaml。
高レベルのコンパイルされた言語が低レベルのコンパイルされた言語と同じくらい高速であることができるというあなたの結論は正しいと間違っています。限界では、コードを最適化して、可能な限り高速に処理を実行できます。ただし、十分にインテリジェントなコンパイラは神話です。人間が読めるコードとマシンコード間の抽象化の層が少ないほど、より直接的な変換が行われます。つまり、コンパイラーはそれほどスマートである必要はありません。つまり、コンパイラーはより優れている場合があります。
Cで書かれたプログラムが、はるかに高水準の言語で書かれたプログラムよりも自動的に高速であると言っているのではありません。高水準言語機能を使用することで得られる利点は、他の(通常はより賢い)人々が、あなたがしていることを最適化する方法をすでに考えていることです。したがって、たとえば、Pythonの組み込みsort
関数をPythonリストで呼び出すと、Cでバブルソートを実装した場合よりもパフォーマンスが向上します。
コメントで述べたように、JavaやC#などの言語はバイトコードに変換され、そのバイトコードはVMによって実行されます。ASM、C、C++などの言語はマシンコードにコンパイルされます。追加のJavaまたはC#がVM内でコードを実行しなければならないため、多くの場合、無視できるほどのオーバーヘッド( ASMのような言語では一般的に高速に実行される特定のタスクです)。
VMで、VMを実行することのオーバーヘッドを打ち消すことができることの1つは、VMがコードをC++のようなマシンコードにコンパイルする言語では、それをコンパイルするとき、一般に、最も一般的な設定のコンパイルオプションを対象にして、プログラムのパフォーマンスが大量のシステムで非常に類似するようにします。C#およびJavaは、プログラムが実行されているシステムの特定の機能を利用できるため、プログラムが実行されているシステムに基づいてプログラムが最高のパフォーマンスを発揮します(C++では、コンパイルする必要があります。これらの機能を利用するための各システムのコード)。
CPU設計者、ハードウェアシステムアーキテクト、コンパイラ作成者は、競合他社のベンチマークを打ち負かすために多くの時間を費やしています。歴史的に、最も公表されているベンチマークのいくつかはFortranとCにありました。そのため、システム全体で最適化されたタイプのコードです。
さらに、高水準言語機能を提供するためにしばしば使用される抽象化のレイヤーは、機械語命令の数だけでなく、キャッシュヒット率、および分岐予測ペナルティにも、ハードウェアレベルで影響を与えることがよくあります。これらもパフォーマンスを低下させる可能性があり、HLL機能の安全性を失うことなく最適化することが非常に困難な場合があります。
高度にオブジェクト指向の言語は、多くの場合、物事をうまくカプセル化しているため、コンパイラーがグローバルに最適化することが難しく、別のパフォーマンスツールが削除されます。
そうは言っても、遅いインタープリターからより高速なVM、ホットスポットJIT、グローバルJIT、トレース駆動型コンパイラー、その他のエキゾチックなコンパイル/並列化トリックに至るまで、任意の言語に適用されるパフォーマンス最適化には大きなばらつきがある可能性があります。さらに、ムーアの法則があります。数十年前に同等の手動でコード化されたCおよびアセンブリ言語よりも高速に実行されるBasicプログラムをゆっくりと解釈しました。そのため、多くの開発者は、最も慣れている言語を使用し、問題を最もうまく解決できると考え、ハードウェアが追いつくのを待ちます。ただし、この考え方には、グローバルコンピューティングのエネルギーと汚染のコストの増加など、他の影響があります。後者は重要だと考える人もいます。