StackOverflowポッドキャストを聞いていると、「本物のプログラマ」がCで書いているジャブが現れ続け、Cは「マシンに近い」ので非常に高速です。前のアサーションを別の投稿に残して、Cが他の言語よりも速くなることを可能にするCの特別な点は何ですか?または、別の言い方をすれば、他の言語がCと同程度の速度で実行されるバイナリにコンパイルできないようにすることは何ですか?
Cについて特別なことはあまりありません。それが高速である理由の1つです。
ガベージコレクション 、 動的タイピング 、およびプログラマがプログラムを簡単に作成できるようにするその他の機能をサポートする新しい言語。
キャッチは、アプリケーションのパフォーマンスを低下させる追加の処理オーバーヘッドがあります。 Cにはそれがありません。つまり、オーバーヘッドはありませんが、プログラマはメモリを割り当てて解放し、 メモリリーク を防ぐ必要があり、静的に対処する必要があることを意味します。変数の入力。
そうは言っても、Java( Java Virtual Machine を使用)や.NET(Common Language Runtimeを使用)などの多くの言語とプラットフォームは、長年にわたってパフォーマンスを向上させてきました。 as ジャストインタイムコンパイル バイトコードからネイティブマシンコードを生成し、パフォーマンスを向上させます。
Cデザイナーが行ったトレードオフがあります。つまり、彼らは速度を安全性よりも高くすることを決定しました。 Cはしません
配列にインデックスを付けると、Javaで、仮想マシンのメソッド呼び出し、バインドチェック、その他の健全性チェックが行われます。それは有効であり、絶対に問題ありません。これは、当然のことながら安全性を高めるためです。しかし、Cでは、ごく些細なことでも安全ではありません。たとえば、Cは、コピーする領域が重複しているかどうかをチェックするためにmemcpyを必要としません。これは、notビッグビジネスアプリケーションをプログラムするための言語として設計されたものではありません。
しかし、これらの設計上の決定は、C言語のバグではありません。コンパイラーとライブラリー作成者がコンピューターからあらゆるパフォーマンスを引き出すことができるように設計されています。 C Rationale ドキュメントで説明されているCの精神は次のとおりです。
Cコードは移植できない場合があります。委員会はプログラマに真に移植可能なプログラムを書く機会を与えようと努力しましたが、委員会はプログラマに強制的に書くことを望みませんでした。移植性の高い、「高レベルアセンブラ」としてのCの使用を排除する:マシン固有のコードを記述する機能は、Cの長所の1つです。
Cの精神を守ります。委員会は、Cの伝統的な精神を維持するための主要な目標として維持しました。Cの精神には多くの側面がありますが、本質は、C言語の基礎となる基本原則のコミュニティ感情です。 Cの精神のファセットのいくつかは、次のようなフレーズで要約できます。
- プログラマを信頼してください。
- プログラマーがやるべきことをするのを妨げないでください。
- 言語を小さくシンプルに保ちます。
- 操作を行う方法を1つだけ提供します。
- ポータブルであることが保証されていない場合でも、高速にしてください。
最後のことわざには少し説明が必要です。効率的なコード生成の可能性は、Cの最も重要な長所の1つです。非常に単純な操作のように見えるものに対してコードの爆発が発生しないようにするため、多くの操作は、ターゲットマシンのハードウェアが行う方法ではなく、一般的な抽象ルール。マシンの動作に対応するこの意欲の例は、式で使用するcharオブジェクトの拡大を管理するルールで見ることができます:charオブジェクトの値が符号付きまたは符号なしの量に拡大するかどうかは、通常、どのバイト操作が多いかに依存しますターゲットマシンで効率的。
0.05秒で実行するCで何かを構築するのに1か月を費やし、Javaで同じことを書いて0.10秒で実行するのに1日費やした場合、Cは本当に高速ですか?
しかし、あなたの質問に答えるために、well-writtenCコードは他の言語のよく書かれたコードよりも速く実行されます。マシンに近いレベルでの手動最適化の実行が含まれます。
コンパイラは確かに非常に賢いものですが、ハンドマッサージアルゴリズムと競合するコードをまだ創造的に考案することはできません(「手」がgoodCプログラマー)。
編集:
多くのコメントは、「私はCで書いており、最適化については考えていません」という行に沿っています。
しかし、具体的な例を挙げると この投稿から :
Delphiでは、これを書くことができます:
function RemoveAllAFromB(a, b: string): string;
var
before, after :string;
begin
Result := b;
if 0 < Pos(a,b) then begin
before := Copy(b,1,Pos(a,b)-Length(a));
after := Copy(b,Pos(a,b)+Length(a),Length(b));
Result := before + after;
Result := RemoveAllAFromB(a,Result); //recursive
end;
end;
そしてCではこれを書きます:
char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (s1[i] == s2[j]) {
break;
}
}
if (j == len2) { /* s1[i] is not found in s2 */
*result = s1[i];
result++; /* assuming your result array is long enough */
}
}
しかし、Cバージョンにはいくつの最適化がありますか?私たちは、Delphiバージョンでは考えていない実装について多くの決定を下します。文字列はどのように実装されますか? Delphiには表示されません。 Cでは、ASCII整数の配列へのポインターであると判断しました。これをcharsと呼びます。 Cでは、キャラクターの存在を1つずつテストします。 Delphiでは、Posを使用します。
そして、これはほんの一例です。大規模なプログラムでは、Cプログラマはコードの数行ごとにこのような低レベルの決定を行う必要があります。手作業で最適化された実行可能ファイルになります。
私はまだそれを見ていなかったので、私はそれを言うでしょう:Cは他のほとんどすべてがC。
JavaはCで構築され、PythonはC(またはJava、または.NETなど)で構築され、Perlはなどです。OSはCで、仮想マシンはCで、コンパイラはCで書かれ、インタプリタはCで書かれています。いくつかのことはまだアセンブリ言語で書かれており、より高速になる傾向があります。 Cで書かれた他の何かで書かれているものが増えています。
(Assemblyではなく)他の言語で記述する各ステートメントは、通常、ネイティブマシンコードにコンパイルされるCの複数のステートメントとしてその下に実装されます。これらの他の言語は、Cよりも高い抽象化レベルを取得するために存在する傾向があるため、Cで必要な追加ステートメントは、安全性の追加、複雑さの追加、エラー処理の提供に重点を置く傾向があります。これらはしばしば良いものですが、costがあり、その名前はspeedおよびsize。
個人的に、私は利用可能なスペクトルのほとんどにまたがる文字通り数十の言語で書いており、あなたが示唆する魔法を個人的に探しました:
どうすればケーキを食べて食べることができますか?私の好きな言語で高レベルの抽象化をどのようにプレイし、スピードを上げるためにCの核心に落とし込むことができますか?
数年の調査の後、私の答えはPython(C上)です。あなたはそれを見てみたいかもしれません。ちなみに、PythonからAssemblyにドロップダウンすることもできます(特別なライブラリの若干の助けを借りて)。
一方、 どの言語でも悪いコードを書くことができます 。したがって、C(またはアセンブリ)コードは自動的に高速ではありません。同様に、いくつかの最適化のトリックにより、高レベル言語コードの部分を未加工Cのパフォーマンスレベルに近づけることができます。人またはハードウェア、したがって違いは本当に重要ではありません。
楽しい。
そこには多くの質問があります-ほとんどは私が答える資格がない質問です。しかし、この最後の1つについて:
他の言語がCと同程度に高速で実行されるバイナリにコンパイルできないようにするために何が必要ですか?
一言で言えば、抽象化。
Cは、機械語から1つまたは2つの抽象レベルだけです。 Javaおよび.Net言語は、アセンブラーから少なくとも3レベルの抽象化されています。 PythonとRubyについてはわかりません。
通常、プログラマーのおもちゃ(複雑なデータ型など)が多いほど、機械語から遠く離れており、より多くの翻訳が必要になります。
私はあちこちにいますが、それが基本的な要点です。
更新 -------詳細については、この投稿にいくつかの良いコメントがあります。
Cのコストモデルは透過的であるため、Cは高速ではありません。 Cプログラムが遅い場合は、明らかに多くのステートメントを実行することで遅くなります。 Cの操作のコストと比較して、オブジェクト(特にリフレクション)または文字列の高レベルの操作には、明らかではないコストがかかる場合があります。
通常、Cと同じくらい高速なバイナリにコンパイルされる2つの言語は、標準ML( MLton コンパイラを使用)と Objective Caml です。 benchmarks game を確認すると、バイナリツリーなどの一部のベンチマークでは、OCamlバージョンがCよりも高速であることがわかります(MLtonエントリは見つかりませんでした)。銃撃を真剣に受け止めます。それが言うように、それはゲームであり、結果はしばしば人々がコードの調整にどれだけの努力をしたかを反映しています。
Cは常に高速ではありません。
Cは、たとえばModern Fortranよりも低速です。
Cは、いくつかの点でJavaよりも遅いことがよくあります。 (特にJITコンパイラーがコードを試した後)
Cはポインターのエイリアシングを発生させます。つまり、いくつかの適切な最適化は不可能です。特に、複数の実行ユニットがある場合、これによりデータフェッチが停止します。わあ.
ポインター演算が機能するという仮定は、一部のCPUファミリー(特にPIC!)でパフォーマンスが大幅に低下する原因になります。これは、セグメント化されたx86で大きなものを使用していました。
基本的に、ベクトルユニットまたは並列化コンパイラを取得すると、Cは悪臭を放ち、最新のFortranはより高速に実行されます。
サンク(実行中の実行可能ファイルの変更)などのCプログラマーのトリックにより、CPUプリフェッチストールが発生します。
あなたはドリフトを取得しますか?
そして、私たちの親友であるx86は、最近の実際のCPUアーキテクチャとはほとんど関係のない命令セットを実行します。シャドウレジスタ、ロードストアオプティマイザー、すべてCPU内。したがって、Cは仮想金属に近くなります。本物の金属、インテルはあなたに見せない。 (歴史的にVLIW CPUは少しバストしたので、それほど悪くないかもしれません。)
高性能DSP(おそらくTI DSP?)でCでプログラミングする場合、コンパイラーは複数の並列実行ユニット間でCを展開するためにいくつかのトリッキーなことをする必要があります。そのため、その場合、Cは金属に近くありませんが、プログラム全体の最適化を行うコンパイラに近いです。奇妙な。
最後に、一部のCPU(www.ajile.com)はハードウェアでJavaバイトコードを実行します。 CはそのCPUで使用するPITAです。
他の言語がCと同程度に高速で実行されるバイナリにコンパイルできないようにするために何が必要ですか?
なし。 Javaや.NET langsのような最新の言語は、パフォーマンスよりもプログラマの生産性を重視しています。ハードウェアは今では安いです。また、中間表現へのコンパイルは、セキュリティ、移植性などの多くのボーナスを与えます。NETCLRは異なるハードウェアを活用できます-たとえば、使用するプログラムを手動で最適化/再コンパイルする必要はありませんSSE命令セットする。
主な要因は、静的に型付けされた言語であり、マシンコードにコンパイルされることです。また、低レベル言語であるため、通常はユーザーが指示しないことは何も行いません。
これらは、思い浮かぶいくつかの他の要因です。
ほとんどの静的型付け言語は、Cと同じくらいまたはそれより高速にコンパイルできます。特に、ポインターのエイリアシングなどのためにCができないと仮定できる場合はそうです。
アセンブリ言語も言語であることを忘れていたと思います:)
しかし、真剣に、Cプログラムは、プログラマーが自分のしていることを知っている場合にのみ高速になります。同じ仕事をする他の言語で書かれたプログラムよりも遅いCプログラムを簡単に書くことができます。
Cが高速である理由は、このように設計されているためです。これにより、コンパイラーがコードを最適化するのに役立つ「下位レベル」の多くのことができます。または、プログラマーがコードを最適化する責任があります。しかし、それはしばしば非常にトリッキーであり、エラーが発生しやすくなります。
他の言語は、すでに言及した他の言語と同様に、プログラマーの生産性により重点を置いています。プログラマーの時間は、マシン時間よりもはるかに高価であると一般に信じられています(昔でも)。したがって、プログラマがプログラムの実行時間ではなく、プログラムの作成とデバッグに費やす時間を最小限に抑えることは非常に理にかなっています。そのためには、多くのことが自動化されているため、プログラムを高速化するためにできることを少し犠牲にします。
C++は平均して高速です(当初はCのスーパーセットでしたが、多少の違いはありますが)。ただし、特定のベンチマークには、より高速な別の言語がしばしばあります。
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
fannjuch-redux
はScalaで最速でした
n-body
とfasta
はAdaで高速でした。
spectral-norm
はFortranで最速でした。
reverse-complement
、mandelbrot
、およびpidigits
は、ATSで最速でした。
regex-dna
はJavaScriptで最速でした。
chameneou-redux
は最速でJava 7.です。
thread-ring
はHaskellで最速でした。
残りのベンチマークは、CまたはC++で最速でした。
おそらくJavaを除いて、Cコンパイラには他のどのコンパイラよりもはるかに多くの努力が注がれているという事実を誰も言及していないと思います。
Cは、すでに述べた多くの理由により、他のどの言語よりも非常に最適化が可能です。したがって、同じ努力が他の言語コンパイラに費やされたとしても、おそらくCがトップになります。
努力によってCよりも最適化できる候補言語が少なくとも1つあると思うので、より高速なバイナリを生成する実装を見ることができます。デジタルMars Dのことを考えているのは、作成者がCよりも最適化できる可能性のある言語を作成することに注意したためです。この可能性がある他の言語があるかもしれません。しかし、最高のCコンパイラーよりもわずか数パーセント以上速いコンパイラーがある言語があるとは想像できません。私は間違っているのが大好きです。
本当の「ぶら下がっている果物」は、人間が最適化するのが簡単になるように設計された言語にあると思います。熟練したプログラマーであれば、どの言語でも高速化できますが、これを実現するには、ばかげたことをしたり、不自然な構成要素を使用したりする必要がある場合があります。常に手間がかかりますが、優れた言語は、プログラムの記述方法を正確に把握する必要なく、比較的高速なコードを生成する必要があります。
最悪の場合のコードは高速になる傾向があることも(少なくとも私にとっては)重要です。 JavaがCと同じかそれより速いというWeb上の多数の「証明」がありますが、それはチェリーピッキングの例に基づいています。私はCの大ファンではありませんが、Cで書いたものは何でもうまくいくことを知っています。 Javaを使用すると、速度の15%以内、通常は25%以内で「おそらく」実行されますが、場合によってはさらに悪化する可能性があります。それが同じくらい高速または数パーセント以内である場合は、通常、とにかく高度に最適化されたライブラリコードに費やされる時間のほとんどが原因です。
これは実際には少しの偽りです。 Cプログラムが頻繁に高速であることは事実ですが、特にCプログラマーがあまり得意でない場合、これは常に当てはまるわけではありません。
人々が忘れがちな大きな明白な穴の1つは、GUIプログラムでのユーザー入力など、何らかのIOのためにプログラムをブロックする必要がある場合です。これらの場合、データを処理する速度よりもデータを取り込む速度によって制限されるため、使用する言語は実際には関係ありません。この場合、C、Java、C#、またはPerlを使用しているかどうかは重要ではありません。データが届くよりも速く進むことはできません。
もう1つの重要な点は、ガベージコレクションを使用し、適切なポインターを使用しないことで、仮想マシンが他の言語では利用できない多くの最適化を行えることです。たとえば、JVMは、ヒープ上でオブジェクトを移動して最適化することができます。これにより、次のインデックスはテーブルで検索するのではなく、単純に使用できるため、将来の割り当てがはるかに高速になります。最新のJVMでは、実際にメモリの割り当てを解除する必要もありません。代わりに、GCとデッドオブジェクトからの使用済みメモリが本質的に無料で回復されるときに、ライブオブジェクトを移動します。
これはまた、Cについての興味深い点をもたらします。C++ではさらに興味深い点をもたらします。 「必要なければ、お金を払わない」という設計哲学があります。問題は、もしあなたがそれを望むなら、あなたはそれを鼻から払うことになってしまうということです。たとえば、Javaのvtable実装はC++実装よりもはるかに優れている傾向があるため、仮想関数呼び出しははるかに高速です。一方で、Javaで仮想関数を使用する以外に選択肢はなく、それでもコストがかかりますが、多くの仮想関数を使用するプログラムでは、コストが削減されます。
これらの回答の多くは、Cが高速である(または高速ではない)理由(一般的または特定のシナリオ)の正当な理由を示しています。次のことは否定できません。
このすべてにもかかわらず、他の多くの言語よりもC言語と他の多くの言語の比較パフォーマンスに大きな影響を与えることに気づいたことがあります。機知に:
多くの場合、他の言語では、実行速度の遅いコードを簡単に作成できます。多くの場合、それは言語の設計哲学によってさえ奨励されています。結果:Cプログラマーは、不必要な操作を実行しないコードを書く可能性が高くなります。
例として、単一のメインウィンドウが作成される単純なWindowsプログラムを考えます。 CバージョンはWNDCLASS[EX]
に渡されるRegisterClass[Ex]
構造体を生成し、CreateWindow[Ex]
を呼び出してメッセージループに入ります。非常に単純化された短縮コードは次のとおりです。
WNDCLASS wc;
MSG msg;
wc.style = 0;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWndCls";
RegisterClass(&wc);
CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
C#の同等のプログラムは、たった1行のコードです。
Application.Run(new Form());
この1行のコードは、ほぼ20行のCコードが提供したすべての機能を提供し、エラーチェックなどのいくつかの機能を追加します。より豊かで充実したライブラリ(典型的なCプロジェクトで使用されるライブラリと比較して)は多くの仕事をしてくれたので、私たちにとっては短いが舞台裏で多くのステップを伴うコードスニペットをさらに書く時間を節約できました。
しかし、簡単で迅速なコードの肥大化を可能にする豊富なライブラリーは、本当に私のポイントではありません。私のポイントは、小さなワンライナーが実際に実行されたときに実際に何が起こるかを調べ始めると、より明確になります。楽しみのために、 。NETソースアクセスを有効にする をVisual Studio 2008以降で使用し、上記の簡単な1行に進みます。あなたが出くわす楽しい小さな宝石の1つは、Control.CreateParams
のゲッターにある次のコメントです。
// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
//
if (createParams == null) {
createParams = new CreateParams();
}
10回。 WNDCLASSEX
構造に格納されているものとCreateWindowEx
に渡されたものの合計にほぼ等しい情報は、Control
クラスから10回before取得されます。 WNDCLASSEX
構造体に格納され、RegisterClassEx
およびCreateWindowEx
に渡されます。
全体として、この非常に基本的なタスクを実行するために実行される命令の数は、C#よりもC#で2〜3桁多くなります。これの一部は、機能が豊富なライブラリの使用によるものです。必要なことを正確に行うだけのCコード。しかし、その一部は、.NETフレームワークのモジュール化されたオブジェクト指向の性質が、手続き型アプローチによってしばしば回避される実行の多くの繰り返しに役立つという事実によるものです。
私はC#や.NETフレームワークを選択しようとはしていません。また、モジュール化、一般化、ライブラリ/言語機能、OOPなどが悪いことだと言っているわけでもありません。私は開発のほとんどをCで、その後C++で、最近ではC#で行っていました。同様に、Cの前は、ほとんどAssemblyを使用していました。そして、言語が「より高く」なるたびに、より短時間でより良く、より保守しやすく、より堅牢なプログラムを作成します。ただし、実行速度が少し遅くなる傾向があります。
ほとんどの場合、すべてのC命令はごく少数のアセンブラー命令に対応しています。基本的に、より高いレベルのマシンコードを記述しているため、プロセッサが実行するほぼすべてを制御できます。 C++などのその他の多くのコンパイル言語には、想像以上に多くのコードに変換できる単純な外観の命令が多数あります(仮想関数、コンストラクターのコピーなど)。また、Javaのような解釈言語またはRubyには、表示されない別の命令層があります-仮想マシンまたはインタープリターです。
私は多くの人がそれを長い風潮で言っていることを知っていますが、
Cは(あなたにとって)少ないので高速です。
言語についてはツールやライブラリほどではありません。 Cで利用可能なライブラリとコンパイラは、新しい言語よりもはるかに古いです。あなたはこれがそれらを遅くするだろうと思うかもしれませんが、反逆です。
これらのライブラリは、処理能力とメモリが限られているときに作成されました。まったく機能するためには、veryを効率的に記述する必要がありました。また、Cコンパイラの開発者は、さまざまなプロセッサ向けにあらゆる種類の巧妙な最適化に取り組むのに長い時間を費やしてきました。 Cの成熟度と幅広い採用により、同年齢の他の言語に比べて大きな利点が得られます。また、Cほど生のパフォーマンスを重視していない新しいツールよりもCに速度の利点があります。
抽象化の欠如は、Cを高速化するものです。出力ステートメントを記述すると、何が起こっているかを正確に把握できます。 Javaで出力ステートメントを記述すると、クラスファイルにコンパイルされ、仮想マシンで実行されて抽象レイヤーが導入されます。言語の一部としてのオブジェクト指向機能の欠如は、生成されるコードが少なくなるほど速度が向上します。 Cをオブジェクト指向言語として使用する場合、クラス、不履行などのすべてのコーディングを行うことになります。つまり、書くだけで十分な量のコードとパフォーマンスペナルティを備えたすべての人に十分な一般化を行います。あなたが仕事を成し遂げるために必要なもの。
最速で実行されるコードは、慎重に手作りされたマシンコードです。アセンブラーもほぼ同じようになります。どちらも非常に低レベルであり、物事を行うには多くのコードを書く必要があります。 Cはアセンブラより少し上です。実際のマシンでは非常に低いレベルで物事を制御する能力がまだありますが、アセンブラーよりも速く簡単に書くための十分な抽象化があります。 C#やJavaなどの他の言語はさらに抽象的です。アセンブラーとマシンコードは低レベル言語と呼ばれますが、C#とJava(および他の多く)は高レベル言語と呼ばれます。 Cは中レベル言語と呼ばれることもあります。
古い「C/C++mustはJavaが解釈されるためJavaよりも速い」という神話を見ると驚くまだ生きていて蹴っている。 数年前の記事 、および 最近のもの があり、その理由を概念または測定で説明します これは必ずしもそうとは限りません 。
現在の仮想マシンの実装(ところでJVMだけでなく)は、プログラムの実行中に収集された情報を利用して、さまざまな手法を使用して、実行中のコードを動的に調整できます。
その他のさまざまな調整 コードが実際に何をしているかを知ること、およびコードが実行されている環境の実際の特性に基づいています。
昔の時代には、コンパイルされた言語と解釈された言語の2種類の言語しかありませんでした。
コンパイルされた言語は、「コンパイラ」を使用して言語構文を読み取り、同一のアセンブリ言語コードに変換します。これは、CPU上で直接実行できるものではありません。解釈された言語はいくつかの異なるスキームを使用していましたが、基本的に言語構文は中間形式に変換され、コードを実行するための環境である「インタープリター」で実行されました。
したがって、ある意味では、コードとマシンの間に別の「レイヤー」、つまりインタープリターがありました。また、コンピューターの場合と同様に、より多くのリソースが使用されることを意味します。通訳者は、より多くの操作を実行しなければならなかったため、遅くなりました。
最近では、コンパイラとインタプリタの両方を使用して動作させるJavaのようなハイブリッド言語が増えています。複雑ですが、JVMは古いインタープリターよりも高速で、洗練されており、最適化されているため、パフォーマンスが(時間の経過とともに)コンパイルされたコードに近いほど優れています。もちろん、新しいコンパイラーには、より凝った最適化のトリックもあるため、以前よりもはるかに優れたコードを生成する傾向があります。しかし、ほとんどの最適化は、ほとんどの場合(常にではありませんが)あらゆるタイプのトレードオフを行い、すべての状況で常に高速になるとは限りません。他のすべてと同様に、無料のものは何もありません。そのため、オプティマイザーはどこかから自慢する必要があります(多くの場合、コンパイル時CPUを使用してランタイムCPUを節約します)。
Cに戻ると、これは単純な言語であり、かなり最適化されたアセンブリにコンパイルして、ターゲットマシンで直接実行できます。 Cでは、整数をインクリメントすると、CPU内のアセンブラーステップが1つだけである可能性が高くなりますが、Javaでは、それよりもはるかに多くなる可能性がありますガベージコレクションも同様です:-) Cは、マシンに非常に近い抽象化を提供します(アセンブラが最も近い)が、それを実現するためにより多くの作業を行う必要があり、保護されておらず、使用またはエラーに優しい。他のほとんどの言語は、より高い抽象化を提供し、基本的な詳細を処理しますが、高度な機能と引き換えに、実行するためにより多くのリソースが必要です。一部のソリューションを一般化すると、より多くのリソースが必要になることが多い、より広範なコンピューティングを処理する必要があります。
ポール。
私はいくつかの言語がより速く、いくつかがより遅い理由についてのリンクで答えを見つけました、私はこれがCまたはC++が他よりも速い理由についてより明確になることを願っています、Cよりも速い他の言語もありますが、それらをすべて使用します。いくつかの説明-
Fortranが重要である大きな理由の1つは高速であるためです。Fortranで記述された数値計算ルーチンは、他のほとんどの言語で記述された同等のルーチンよりも高速である傾向があります。この分野でFortranと競合している言語(CとC++)は、このパフォーマンスと競合するため使用されます。
これは疑問を提起します:なぜですか? C++とFortranを高速化するのはなぜですか?また、JavaやPythonなどの他の一般的な言語よりも優れているのはなぜですか?
解釈とコンパイルプログラミング言語が推奨するプログラミングのスタイルと提供する機能に応じて、プログラミング言語を分類および定義する方法は多数あります。パフォーマンスを見ると、最大の違いはインタープリター言語とコンパイル済み言語の違いです。
分割は難しくありません。むしろ、スペクトルがあります。一端に、Fortran、C、C++を含むグループである従来のコンパイル言語があります。これらの言語には、プログラムのソースコードをプロセッサが使用できる実行可能形式に変換する個別のコンパイルステージがあります。
このコンパイルプロセスにはいくつかのステップがあります。ソースコードが分析および解析されます。この時点で、タイプミスやスペルミスなどの基本的なコーディングミスを検出できます。解析されたコードはメモリ内表現を生成するために使用されます。これは、ミスの検出にも使用できます。今回は、存在しない関数の呼び出しやテキスト文字列の算術演算の実行などのセマンティックミスです。
このメモリ内表現は、実行可能なコードを生成する部分であるコードジェネレーターを駆動するために使用されます。生成されたコードのパフォーマンスを改善するためのコード最適化は、このプロセス内のさまざまな時点で実行されます。コード表現で高レベルの最適化を実行でき、コードジェネレーターの出力で低レベルの最適化が使用されます。
実際にコードを実行することは後で起こります。コンパイルプロセス全体は、単に実行可能なものを作成するために使用されます。
反対に、通訳がいます。インタープリターには、コンパイラーの構文解析ステージと同様の構文解析ステージが含まれますが、これを使用して、プログラムをすぐに実行し、直接実行を駆動します。
最も単純なインタープリターには、言語がサポートするさまざまな機能に対応する実行可能コードが含まれています。そのため、特定の言語が持つものに関係なく、数字を追加したり、文字列を結合したりできますコードを解析するときに、対応する関数を検索して実行します。プログラムで作成された変数は、名前をデータにマップする何らかのルックアップテーブルに保持されます。
インタプリタスタイルの最も極端な例は、バッチファイルまたはシェルスクリプトのようなものです。これらの言語では、多くの場合、実行可能コードはインタープリター自体に組み込まれているのではなく、別個のスタンドアロンプログラムです。
では、なぜこれがパフォーマンスに違いをもたらすのでしょうか?一般に、インダイレクションの各レイヤーはパフォーマンスを低下させます。たとえば、2つの数値を追加する最も速い方法は、これらの数値の両方をプロセッサのレジスタに入れて、プロセッサの加算命令を使用することです。それがコンパイルされたプログラムができることです。変数をレジスターに入れて、プロセッサー命令を活用できます。しかし、解釈されたプログラムでは、同じ追加を行うには、追加する値をフェッチするために変数のテーブルで2回ルックアップし、追加を実行する関数を呼び出す必要があります。その関数は、コンパイルされたプログラムが実際の加算を実行するために使用するのと同じプロセッサ命令を使用する可能性がありますが、命令が実際に使用される前のすべての余分な作業が遅くなります。
もっと知りたい場合は Source をチェックしてください。
誰かのWordを引き受けないでください。コードのパフォーマンスに重要な部分で、Cと選択言語の両方の逆アセンブリを見てください。 Visual Studioの実行時の逆アセンブリウィンドウを見るだけで、逆アセンブルされた.Netを確認できると思います。 windbgを使用してJavaに注意が必要な場合は可能ですが、.Netでそれを行う場合、多くの問題は同じです。
必要がなければCで書くのは好きではありませんが、Cで同じルーチンを逆アセンブルするだけで、C以外の言語の速度を宣伝するというこれらの回答でなされた主張の多くは無視できると思います。特に、パフォーマンスが重要なアプリケーションでよく見られるように、大量のデータが関係する場合は、選択した上位レベルの言語で。 Fortranは、その専門分野では例外かもしれませんが、知りません。 Cよりも高いレベルですか?
初めてJITされたコードとネイティブコードを比較したところ、.NetコードがCコードと同等に実行できるかどうかという疑問がすべて解決しました。追加の抽象化レベルとすべての安全性チェックには、かなりの費用がかかります。同じコストがおそらくJavaにも当てはまりますが、私のWordをそれとは思わず、パフォーマンスが重要な場所で試してみてください。 (JITed Javaについて十分に知っている人なら誰でも、メモリ内のコンパイル済みプロシージャを見つけることができますか?
それは自動と手動の違いであり、高レベルの言語は抽象化されているため自動化されています。 C/C++は手動で制御および処理され、エラーチェックコードでさえ手作業である場合があります。
CとC++もコンパイルされた言語であるため、どこでもビジネスを行うことはできません。これらの言語は、使用するハードウェアに合わせて微調整する必要があるため、余分なレイヤーを追加する必要があります。 C/C++コンパイラがすべてのプラットフォームでより一般的になりつつあるため、これは今ややかすれています。プラットフォーム間でクロスコンパイルを実行できます。それはどこでも実行される状況ではなく、基本的にコンパイラAに同じコードの異なるアーキテクチャのコンパイラBに対してコンパイルするよう指示します。
要するに、C言語は理解しやすく、理由付けが簡単ではありません。これが、システム言語と呼ばれる理由でもあります。これらは、このような高レベルの抽象化ナンセンスの前に登場しました。これが、フロントエンドWebプログラミングに使用されない理由でもあります。彼らはタスクに適さず、従来の言語ツールでは解決できない複雑な問題を解決することを意味します。
これが、マイクロアーキテクチャ、ドライバー、量子物理学、AAAゲーム、オペレーティングシステムなど、CやC++が適しているものなど、おかしなものを入手する理由です。速度と数値計算が主要な領域です。
Cはネイティブにコンパイルされた低レベル言語であるため高速です。しかし、Cは最速ではありません。 再帰フィボナッチベンチマーク は、Rust、Crystal、およびNimの方が高速であることを示しています。
一部のC++アルゴリズムはCよりも高速であり、他の言語のアルゴリズムまたは設計パターンの一部の実装はCよりも高速です。
Cが速いと言ってから、他の言語の話に移るとき、彼らは一般にベンチマークとしてCのパフォーマンスを使用しています。
ホットスポットの最適化 、 プリコンパイルされたメタアルゴリズム 、およびさまざまな形式の 並列化 などの高度な最適化手法を脇に置いて、言語は、 内部ループ 内で一般的に指定される操作をサポートするために必要な暗黙的な舞台裏の複雑さと強く相関します=。
おそらく最も明白なのは、null
のポインターのチェックや配列境界に対するインデックスのチェックなど、間接メモリ参照の有効性チェックです。ほとんどの高水準言語はこれらのチェックを暗黙的に実行しますが、Cは実行しません。ただし、これは必ずしもこれらの他の言語の基本的な制限ではありません-十分に賢いコンパイラーは、何らかの形でアルゴリズムの内部ループからこれらのチェックを削除できる可能性があります- ループ不変コードの動き 。
C(および同様に密接に関連するC++)のより基本的な利点は、 スタックベースのメモリ割り当て に大きく依存していることです。これは本質的に高速です割り当て、割り当て解除、およびアクセス用。 C(およびC++)では、プライマリ コールスタック は、プリミティブ、配列、および集約の割り当てに使用できます(struct
/class
)。
Cは 動的に割り当てる 任意のサイズと有効期間のメモリ(いわゆる「ヒープ」を使用)の機能を提供しますが、これはデフォルトで回避されます(代わりにスタックが使用されます)。
そもそも、他のプログラミング言語のランタイム環境内でCメモリ割り当て戦略を複製することも可能です。これは asm.js で実証されており、CまたはC++で記述されたコードをJavaScriptのサブセットに変換し、Webブラウザー環境で安全に実行できるようにします(ネイティブに近い速度)。
余談ですが、CとC++が他のほとんどの言語よりも優れているもう1つの分野は、ネイティブマシンの命令セットとシームレスに統合できることです。これの顕著な例は、(コンパイラとプラットフォームに依存する) SIMD組み込み関数 の可用性です。これは、ほぼユビキタスな並列処理ハードウェアを活用するカスタムアルゴリズムの構築をサポートしながら、データ割り当てを利用します言語によって提供される抽象化(低レベルのレジスター割り当てはコンパイラーによって管理されます)。
1)他の人が言ったように、Cはあなたのためにあまりしません。初期化変数、配列境界チェック、メモリ管理などはありません。他の言語の機能には、Cが費やさないメモリとCPUサイクルがかかります。
2)Cの抽象化が少なく、したがって高速であるという回答は、半分しか正しいと思います。技術的に言えば、言語Xに「十分に高度なコンパイラ」があれば、言語XはCの速度に近づくか、またはそれに等しくなります。Cとの違いは、Cが非常に明らかにマップするためです素朴なコンパイラでさえまともな仕事をすることができるアセンブリ言語に直接。 Pythonのようなものの場合、オブジェクトの可能性のあるタイプを予測し、その場でマシンコードを生成するための非常に高度なコンパイラが必要です-Cのセマンティクスは、単純なコンパイラでうまくいくほど単純です。
最新の最適化コンパイラでは、純粋なCプログラムがコンパイルされた.netコードよりもはるかに高速になる可能性は非常に低いです。 .netなどのフレームワークが開発者に提供する生産性の向上により、通常のCでは数週間または数か月かかっていた作業を1日で行うことができます。開発者の給与と比較してハードウェアの安価なコストと相まって、それはただ方法高水準言語で記述したり、ハードウェアをスローしたりする方が安価です。
ジェフとジョエルがCを「本当のプログラマー」言語であると言っているのは、Cに手を持たないからです。自分でメモリを割り当てたり、そのメモリの割り当てを解除したり、境界チェックをしたりする必要があります。 as new object();ガベージコレクション、クラス、OOP、エンティティフレームワーク、LINQ、プロパティ、属性、フィールドなどはありません。ポインター演算やポインターの逆参照方法などを知っておく必要があります。そして、その問題については、ポインターが何であるかを知って理解してください。スタックフレームとは何か、命令ポインタは何かを知る必要があります。作業しているCPUアーキテクチャのメモリモデルを知る必要があります。 Cでプログラミングするとき、C#やJavaのようなものでプログラミングするとき、単に存在しないか、必要ではない場合、マイクロコンピューター(通常はthe作業中のマイクロコンピューター)のアーキテクチャーについて多くの暗黙の理解があります。その情報はすべて、コンパイラ(またはVM)プログラマにオフロードされています。
実際、特定のアプリケーション(数値)では、Cでさえ打ち負かされる可能性があります。アセンブリ言語を意味するのではなく、古くからrid笑されるFortranを意味します。その理由は、Fortranはポインターのエイリアシングがないことを保証するからです。
IDEでマシンコードをステップ実行するだけで、なぜ高速なのかがわかります(高速の場合)。それは多くの手持ちを除外します。また、Cxxを除外するように指示することもできますが、その場合はほぼ同じです。
コンパイラーの最適化は、言語速度に関するほとんどすべての認識と同様に過大評価されています。
生成されたコードの最適化は、ホットスポットコードの違い、つまり、関数呼び出し(明示的または暗黙的)のない厳密なアルゴリズムのみに影響します。それ以外では、ほとんど何も達成しません。
時間と労力がすべてです。
無限の時間と労力が与えられた場合:
一定の時間と労力を考えると:
どうして?抽象化を行うほど、実際に重要なコードの重要なセクションの最適化に費やすことができる時間が長くなるためです。ここでのいくつかの仮定は、開発者が3つすべての言語で同等に有能であり、バイナリサイズ、メモリ使用量などを気にしないということです。
すべての抽象化には、パフォーマンス面でコストがかかりますが、コードの記述を簡単かつ高速にする必要があります。
CとC++の違いでさえ、時には大きくなることがあります。
オブジェクトにメモリを割り当てたり、コンストラクタを呼び出したり、Wordの境界にメモリを配置したりすると、プログラムは、プログラマから引き離された多くのオーバーヘッドを通過します。
Cを使用すると、プログラムが実行している各処理を、通常は非常に詳細なレベルで強制的に確認できます。これにより、当面の目標には不要な多くのタスクを実行するコードを書くことは(決して不可能ではありませんが)より難しくなります。
したがって、たとえばBASICプログラムでは、INPUTキーワードを使用して文字列をSTDINから読み取り、変数にメモリを自動的に割り当てますが、Cでは、プログラマは通常、既にメモリを割り当てており、プログラムがIをブロックするかどうかなどを制御できます/ Oかどうか、および必要な情報を取得した後に入力の読み取りを停止するか、行の終わりまで文字の読み取りを続行する場合。
また、Cは他の言語よりもエラーチェックがはるかに少ないため、プログラマが何をしているのかをプログラマが知っていると仮定します。 PHPでは、文字列$myStr = getInput();
を宣言して$myStr[20]
を参照するが、入力が10文字しかなかった場合、PHPはこれをキャッチします安全に空の文字列を返します。 Cは、文字列の終わりを超えてデータを保持するのに十分なメモリが割り当てられているか、文字列の後にどんな情報があり、代わりにそれを参照しようとしていると仮定します。これらの小さな要因は、全体としてオーバーヘッドに大きな影響を及ぼします。