StackOverflowポッドキャストを聞いていると、ジャブは「本物のプログラマ」がCで書いていることを思い起こし、Cは「マシンに近い」ため、はるかに高速です。以前のアサーションを別の投稿に残して、Cが他の言語よりも高速になることを可能にする特別な点は何ですか?または別の言い方をすると:他の言語がCと同じくらい高速に実行されるバイナリにコンパイルできないようにする方法は何ですか?
StackOverflowポッドキャストを聞いていると、ジャブは「本物のプログラマ」がCで書いていることを思い起こし、Cは「マシンに近い」ため、はるかに高速です。以前のアサーションを別の投稿に残して、Cが他の言語よりも高速になることを可能にする特別な点は何ですか?または別の言い方をすると:他の言語がCと同じくらい高速に実行されるバイナリにコンパイルできないようにする方法は何ですか?
回答:
Cについて特別なことはそれほど多くありません。それが高速である理由の1つです。
ガベージコレクション、動的型付け、およびプログラマーによるプログラムの作成を容易にするその他の機能をサポートする新しい言語。
問題は、アプリケーションのパフォーマンスを低下させる追加の処理オーバーヘッドがあることです。Cにはそれがありません。つまり、オーバーヘッドはありませんが、プログラマーは、メモリリークを防ぐためにメモリを割り当てて解放する必要があり、変数の静的型付けを処理する必要があります。
とはいえ、Java(Java仮想マシンを使用)や.NET(共通言語ランタイムを使用)などの多くの言語やプラットフォームは、ネイティブマシンコードを生成するジャストインタイムコンパイルなどの登場により、長年にわたってパフォーマンスが向上しています。より高いパフォーマンスを達成するためのバイトコード。
Cデザイナーが行ったトレードオフがあります。つまり、安全性よりも速度を優先することにしました。Cはしません
配列にインデックスを付ける場合、Javaでは、仮想マシンでのメソッド呼び出し、境界チェック、その他の健全性チェックが必要です。それは正当であり、絶対に問題ありません。なぜなら、それが原因で安全性が追加されるからです。しかし、Cでは、ごく些細なことでさえ安全にはなりません。たとえば、Cはmemcpyにコピーする領域が重複しているかどうかを確認するように要求しません。それはだていない大企業のアプリケーションをプログラミングするための言語として設計されています。
ただし、これらの設計上の決定はC言語のバグではありません。コンパイラーやライブラリーの作成者がコンピューターからあらゆるパフォーマンスを引き出すことができるため、これらは仕様によるものです。Cの根拠となるドキュメントで説明されているCの精神は次のとおりです。
Cコードは移植できません。プログラマに真に移植可能なプログラムを作成する機会を与えることを目指しましたが、委員会はCを「高レベルのアセンブラ」として使用することを排除するために、プログラマに移植性のある書き込みを強制したくありませんでした:マシン固有の書き込み機能コードはCの長所の1つです。
Cの精神を維持します。委員会は、Cの伝統的な精神を維持するための主要な目標として維持しました。Cの精神には多くの側面がありますが、本質は、C言語の基礎となる基本原則のコミュニティの感情です。Cの精神のいくつかの側面は、次のようなフレーズで要約できます。
- プログラマーを信頼してください。
- プログラマーが必要なことをするのを妨げないでください。
- 言語は小さくシンプルにしてください。
- 操作を行う1つの方法のみを提供します。
- ポータブルであることが保証されていない場合でも、高速にしてください。
最後のことわざには少し説明が必要です。効率的なコード生成の可能性は、Cの最も重要な長所の1つです。非常に単純な操作のように見えるものに対してコードの爆発が起こらないようにするために、多くの操作は、ターゲットマシンのハードウェアがそれを行う方法ではなく、一般的な抽象ルール。マシンが何をするかというこの意欲の例は、式で使用するcharオブジェクトの拡張を制御するルールに見ることができます。charオブジェクトの値が符号付きまたは符号なしの量に拡張されるかどうかは、通常、より多くのバイト操作に依存します。ターゲットマシンで効率的です。
Cで何かを構築するのに1か月を費やして0.05秒で実行し、1日をJavaで同じものを書いて0.10秒で実行する場合、Cは本当に高速ですか?
しかし、あなたの質問に答えるために、Cコードを「よく」書くことの一部には、マシンに近いレベルで手動の最適化を行うことが含まれるため、よく書かれた Cコードは、他の言語のよく書かれたコードよりも速く実行されます。
コンパイラーは確かに非常に賢いですが、手で管理されたアルゴリズムと競合するコードを創造的に生み出すことはまだできていません(「手」は優れた Cプログラマーに属していると想定しています)。
編集:
「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;
そしてCIでこれを書いてください:
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では、charと呼ばれるASCII整数の配列へのポインターになることを決定しました。Cでは、キャラクターの存在を1つずつテストします。Delphiでは、Posを使用します。
そして、これはほんの小さな例です。大規模なプログラムでは、Cプログラマーは数行のコードごとにこの種の低レベルの決定を行わなければなりません。その結果、手作りで最適化された実行可能ファイルが作成されます。
私はまだそれを見ていなかったので、私はそれを言います 。他のほとんどすべてがCで書かれているため、Cはより高速になる傾向があります。
JavaはCに基づいて構築され、PythonはC(またはJava、.NETなど)に基づいて構築され、Perlはその他などです。OSはCで記述され、仮想マシンはCで記述され、コンパイラはCで記述されます。インタプリタはCで書かれています。いくつかはまだアセンブリ言語で書かれていますが、さらに高速になる傾向があります。ますます多くのものが別の何かで書かれています。それ自体がCで書かれています。
他の言語(アセンブリではない)で記述する各ステートメントは、通常、Cのいくつかのステートメントの下に実装され、ネイティブマシンコードにコンパイルされます。これらの他の言語は、Cよりも高いレベルの抽象化を実現するために存在する傾向があるため、Cで必要なこれらの追加のステートメントは、安全性の追加、複雑さの追加、およびエラー処理の提供に重点が置かれる傾向があります。それらはしばしば良いものですが、コストがかかり、その名前はスピードとサイズです。
個人的に、私は文字通り何十もの言語で利用可能なスペクトルのほとんどを網羅して書いており、私は個人的にあなたがヒントとする魔法を求めてきました:
どうすればケーキも食べられますか?どうすればお気に入りの言語で高レベルの抽象化を試してから、スピードを上げるためにCの要点に落とすことができますか?
数年の研究の後、私の答えはPython(on C)です。あなたはそれを見てみたいかもしれません。ちなみに、Pythonからアセンブリーにドロップダウンすることもできます(特別なライブラリーからのマイナーな助けが必要です)。
一方、不正なコードはどの言語でも記述できます。したがって、C(またはアセンブリ)コードは自動的に高速にはなりません。同様に、一部の最適化トリックでは、高水準言語コードの一部を生のCのパフォーマンスレベルに近づけることができます。しかし、ほとんどのアプリケーションでは、プログラムはほとんどの時間を人やハードウェアで待機するため、違いは実際には問題になりません。
楽しい。
そこにはたくさんの質問があります-ほとんど私が答える資格がない質問です。しかし、この最後のものについて:
他の言語がCと同じくらい高速に実行されるバイナリにコンパイルできないようにする方法は何ですか?
つまり、抽象化です。
Cは機械語から1レベルまたは2レベルの抽象化レベルにすぎません。Javaおよび.Net言語は、アセンブラーから少なくとも3レベルの抽象化レベルにあります。PythonとRubyについてはよくわかりません。
通常、プログラマーのおもちゃ(複雑なデータ型など)が多ければ多いほど、機械語から遠くなり、より多くの翻訳を行う必要があります。
私はあちこち離れていますが、それが基本的な要点です。
更新 -------この投稿には、より詳細なコメントが含まれています。
Cのコストモデルが透過的であるため、Cが高速であることはそれほど多くありません。Cプログラムが遅い場合、多くのステートメントを実行することにより、明らかに遅いです。Cでの操作のコストと比較すると、オブジェクト(特にリフレクション)または文字列に対する高レベルの操作には、明白ではないコストがかかる場合があります。
一般にCと同じくらい高速なバイナリにコンパイルされる2つの言語は、標準ML(MLtonコンパイラーを使用)とObjective Camlです。ベンチマークゲームを確認すると、バイナリツリーなどの一部のベンチマークでは、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言語などの最近の言語は、パフォーマンスよりもプログラマの生産性を重視しています。ハードウェアは今日安いです。また、中間表現へのコンパイルは、セキュリティ、移植性などの多くのボーナスを提供します。.NETCLRは、さまざまなハードウェアを利用できます。たとえば、SSE命令セットを使用するためにプログラムを手動で最適化/再コンパイルする必要はありません。
主な要因は、静的に型付けされた言語であり、マシンコードにコンパイルされていることです。また、低水準言語であるため、通常、ユーザーが指示しないことは何も行いません。
これらは頭に浮かぶいくつかの他の要因です。
ほとんどの静的型付き言語は、Cと同じかそれより高速にコンパイルできますが、特にポインタのエイリアスなどのためにCができないと仮定できる場合はなおさらです。
アセンブリ言語も言語であることを忘れていたと思います:)
しかし真剣に、Cプログラムは、プログラマーが何をしているかを知っている場合にのみ高速になります。同じ仕事をする他の言語で書かれたプログラムよりも実行速度が遅いCプログラムを簡単に書くことができます。
Cの方が高速である理由は、Cがこのように設計されているためです。コンパイラーがコードを最適化するのに役立つ多くの「低レベル」のことを行うことができます。または、プログラマーがコードを最適化する責任を負うとしましょう。しかし、それはしばしばかなりトリッキーでエラーが発生しやすくなります。
他の言語は、すでに述べた他の言語と同様に、プログラマーの生産性により重点を置いています。プログラマーの時間は、マシンの時間よりもはるかに高価であると一般的に信じられています(昔でも)。したがって、プログラマがプログラムの実行時間ではなく、プログラムの作成とデバッグに費やす時間を最小限に抑えることは、理にかなっています。そのためには、多くのことが自動化されているため、プログラムを高速化するためにできることを少し犠牲にします。
ほとんどの場合、すべてのC命令はごく少数のアセンブラー命令に対応しています。あなたは本質的に高レベルのマシンコードを書いているので、プロセッサが行うほとんどすべてのものを制御できます。C ++など、他の多くのコンパイル済み言語には、見た目よりもはるかに多くのコードに変換できる単純な命令がたくさんあります(仮想関数、コピーコンストラクターなど)。JavaやRubyなどのインタープリター型言語には、別のレイヤーがあります。表示されない指示-仮想マシンまたはインタープリター。
C ++は平均して高速です(当初はCのスーパーセットでしたが、いくつかの違いはあります)。ただし、特定のベンチマークについては、より高速な別の言語がよくあります。
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
fannjuch-redux
Scalaで最速だった
n-body
そしてfasta
速くエイダでいました。
spectral-norm
Fortranで最速でした。
reverse-complement
、mandelbrot
およびpidigits
ATSで最速でした。
regex-dna
JavaScriptで最速でした。
chameneou-redux
最も速かったのはJava 7です。
thread-ring
ハスケルで最速だった。
残りのベンチマークは、CまたはC ++で最も高速でした。
extern "C"
C ++があることとは何の関係もありませんスーパーセット C.の
system("bash script.sh");
、どのbashスクリプトでも機能すると言っているようなもので、Cはbashのスーパーセットです。extern "C"
名前マングリングのために、C ++でCリンケージを提供します。XをYのスーパーセットとして呼び出すことは、Yで実行できるすべてのことをXでも実行できることを意味しますが、これはC ++には当てはまりません。Cでは有効であるがC ++では無効な言語構成要素がかなりあります。
bash
は、使用可能なコマンドラインプログラムを義務付けるものはありません。サポートしておく必要があるbashのバージョン/仕様が含まれている場合、それをスーパーセットと見なします。
struct foo { int: this; }; typedef float foo;
これらの回答の多くは、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プロジェクトで使用されるライブラリと比較して)豊富で充実したライブラリは私たちにとって多くの作業を行い、私たちには、見た目が悪いが裏で多くのステップを含む多くのコードスニペットを書く時間を解放しました。
しかし、簡単で迅速なコードの膨張を可能にする豊富なライブラリーは、私の趣旨ではありません。私の小さなワンライナーが実際に実行されるときに実際に何が起こるかを調べ始めると、私のポイントはより明白になります。楽しみのために、Visual Studio 2008以降で.NETソースアクセスを有効にし、上記の簡単な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時間前にそれがで保存されているWNDCLASSEX
構造とに渡さRegisterClassEx
とCreateWindowEx
。
全体として、この非常に基本的なタスクを実行するために実行される命令の数は、C#よりもC#の方が2〜3桁多くなります。これの一部は、機能が豊富なライブラリの使用によるものです。必要なことを正確に実行し、それ以上は実行しない単純なCコード。しかし、その一部は、.NETフレームワークのモジュール化されたオブジェクト指向の性質が、手続き型のアプローチによって回避されることが多い多くの実行の繰り返しに役立っているという事実によるものです。
C#や.NET Frameworkを選択するつもりはありません。モジュール化、一般化、ライブラリ/言語機能、OOPなどが悪いことだとも言っていません。私は開発の大部分をCで、後でC ++で、そして最近ではC#で行っていました。同様に、C以前は、ほとんどアセンブリを使用していました。そして、私の言語が「高く」なる各ステップで、私はより短い時間でより良い、より保守しやすく、より堅牢なプログラムを書きます。ただし、実行速度は少し遅くなる傾向があります。
おそらくJavaを除いて、他のコンパイラーよりもCコンパイラーに多くの労力が費やされたという事実について誰も言及しなかったと思います。
Cは、すでに述べた多くの理由で、他のほとんどの言語よりも非常に最適化可能です。したがって、他の言語コンパイラに同じ量の労力が費やされたとしても、Cはおそらくトップに立つでしょう。
少なくとも1つの候補言語があり、努力を重ねればCよりも最適化できるため、より高速なバイナリを生成する実装を見ることができます。デジタルマーズDについて考えています。Cよりも最適化される可能性のある言語を作成者が作成することに注意を払ったからです。この可能性のある他の言語があるかもしれません。しかし、どの言語でも、最高のCコンパイラよりも数パーセント以上高速なコンパイラを実現できるとは思えません。私は間違っていたいです。
本当の「ぶら下がり果物」は、人間が最適化するのが簡単になるように設計された言語であると思います。熟練したプログラマーであれば、どの言語でも高速に処理できますが、これを実現するために、とんでもないことをしたり、不自然な構造を使用したりする必要がある場合があります。常に努力が必要ですが、優れた言語は、プログラムの記述方法に厳密にこだわる必要なく、比較的高速なコードを生成する必要があります。
最悪の場合のコードが高速になる傾向があることも(少なくとも私にとって)重要です。JavaはCと同じかそれより速いというWeb上の「証明」は数多くありますが、それはチェリーピッキングの例に基づいています。私はCの大ファンではありませんが、Cで書いたものはすべてうまくいくことを知っています。Javaを使用すると、「おそらく」速度の15%以内、通常は25%以内で実行されますが、場合によってはそれよりはるかに悪いこともあります。それが同じくらい高速であるか、数パーセント以内である場合は通常、とにかくCで大幅に最適化されているライブラリコードに費やされている時間のほとんどが原因です。
これは実際には永続的な虚偽のビットです。Cプログラムの方が高速であることが多いのは事実ですが、これは必ずしも当てはまるわけではありません。特にCプログラマーがあまり上手ではない場合はなおさらです。
人々が忘れがちな大きな大きな穴の1つは、プログラムが何らかのGUIプログラムでのユーザー入力など、何らかのIOをブロックしなければならない場合です。これらの場合、データの処理速度ではなく、データの受信速度によって制限されるため、使用する言語は問題ではありません。この場合、C、Java、C#、さらにはPerlを使用しているかどうかはそれほど重要ではありません。データが入ってくるよりも速く行くことはできません。
もう1つの重要な点は、ガベージコレクションを使用し、適切なポインターを使用しないことで、仮想マシンが他の言語では利用できない多くの最適化を行えるようになることです。たとえば、JVMはオブジェクトをヒープ上で移動させてデフラグすることができます。これにより、テーブルで検索するのではなく、次のインデックスを簡単に使用できるため、将来の割り当てがはるかに速くなります。最近のJVMは、実際にメモリの割り当てを解除する必要もありません。代わりに、GCを実行するときにライブオブジェクトを移動するだけで、デッドオブジェクトから消費されたメモリは基本的に無料で回復されます。
これはまた、Cについて興味深い点をもたらします。C++の場合はなおさらです。「必要ないのなら、お金を払わない」という設計哲学のようなものがあります。問題は、あなたがそれを望めば、結局それを鼻から払ってしまうことです。たとえば、Javaのvtable実装はC ++実装よりもはるかに優れている傾向があるため、仮想関数呼び出しははるかに高速です。一方、Javaで仮想関数を使用する以外に選択肢はなく、それでもいくらかコストがかかりますが、仮想関数を多く使用するプログラムでは、コストが削減されます。
それは、ツールやライブラリーほど言語についてではありません。Cで使用できるライブラリとコンパイラは、新しい言語よりもはるかに古くなっています。あなたはこれが彼らを遅くすると思うかもしれませんが、反対です。
これらのライブラリは、処理能力とメモリが非常に貴重なときに作成されました。それらは、まったく機能するために非常に効率的に書かれなければなりませんでした。Cコンパイラの開発者も、さまざまなプロセッサ向けにあらゆる種類の巧妙な最適化に取り組む長い時間を費やしてきました。Cの成熟度と幅広い採用により、同じ時代の他の言語に比べて大きな利点があります。また、Cが必要とするほどの生のパフォーマンスを重視しない新しいツールよりも、Cに速度上の利点があります。
抽象化の欠如は、Cをより速くするものです。出力ステートメントを書くと、何が起こっているのか正確にわかります。Javaで出力ステートメントを記述する場合、クラスファイルにコンパイルされ、抽象化の層を導入する仮想マシンで実行されます。言語の一部としてのオブジェクト指向機能の欠如により、生成されるコードが少なくなるため、速度も向上します。Cをオブジェクト指向言語として使用している場合は、クラス、非侵害などのすべてのコーディングを行っています。これは、コード量と、書き込みのみを必要とするパフォーマンスペナルティを備えたすべての人にとって十分に一般化されたものを作成することを意味しますあなたが仕事を成し遂げるために必要なもの。
古い「Javaが解釈されるため、C / C ++ は Javaよりも高速である必要がある」という神話は驚くべきものです。数年前の記事や最近の記事があり、これが必ずしも常にそうであるとは限らない理由や概念を説明しています。
現在の仮想マシンの実装(ちなみに、JVMだけでなく)は、プログラムの実行中に収集された情報を利用して、さまざまな手法を使用して、実行時にコードを動的に調整できます。
そして、コードが実際に何をしているかを知ること、およびコードが実行されている環境の実際の特性に基づいて、他のさまざまな調整を行います。
最速で実行されるコードは、注意深く手作りされたマシンコードです。アセンブラはほぼ同じです。どちらも非常に低レベルであり、処理を行うには多くのコードを書く必要があります。Cはアセンブラより少し上です。実際のマシンでは非常に低いレベルで物事を制御する能力はまだありますが、アセンブラよりも速くて簡単に書くための十分な抽象化があります。C#やJAVAなどの他の言語は、より抽象的です。アセンブラーとマシンコードは低水準言語と呼ばれますが、C#とJAVA(およびその他の多く)は高水準言語と呼ばれます。Cは中級言語と呼ばれることもあります。
誰かの言葉を使わないでください。コードのパフォーマンスが重要な部分で、Cと選択した言語の両方の逆アセンブリを確認してください。 Visual Studioの実行時に逆アセンブリウィンドウを見るだけで、逆アセンブルされた.Netを確認できると思います。windbgを使用してJavaにトリッキーな場合は可能ですが、.Netでそれを行う場合、問題の多くは同じです。
必要がなければCで書くのは好きではありませんが、C以外の言語の速度を宣伝するこれらの回答で行われた主張の多くは、Cで同じルーチンを逆アセンブルするだけで脇に置くことができると思います選択した上位レベルの言語で、特にパフォーマンスが重要なアプリケーションで一般的であるように大量のデータが含まれる場合。Fortranはその専門分野の例外かもしれませんが、わかりません。Cより高いレベルですか?
初めて、JITされたコードとネイティブコードを比較して、.NetコードがCコードと同等に実行できるかどうかのすべての質問を解決しました。追加レベルの抽象化とすべての安全性チェックにはかなりのコストがかかります。同じコストがJavaに適用される可能性がありますが、私の言葉にとらわれず、パフォーマンスが重要な場所で試してください。(誰でも、JITされたJavaについて十分に知っていて、コンパイルされたプロシージャをメモリ内で見つけることができますか?確かに可能です)
1)他の人が言ったように、Cはあなたのためにもっと少ないことをします。初期化変数、配列境界チェック、メモリ管理などはありません。他の言語のこれらの機能は、Cが費やさないメモリとCPUサイクルを消費します。
2)Cは抽象化されていないため、より高速であると答えるのは、正解の半分に過ぎないと思います。技術的に言えば、言語Xの「十分に高度なコンパイラ」を使用している場合、言語XはCの速度に近づくか、または同等にすることができます。Cとの違いは、(アーキテクチャコースを受講した場合)素朴なコンパイラでさえまともな仕事をすることができるアセンブリ言語に直接。Pythonのようなものでは、可能性のあるオブジェクトのタイプを予測し、その場でマシンコードを生成するための非常に高度なコンパイラが必要です。Cのセマンティクスは非常に単純で、単純なコンパイラで十分に機能します。
古き良き時代には、コンパイルと解釈の2種類の言語しかありませんでした。
コンパイルされた言語は、「コンパイラ」を利用して言語構文を読み取り、それを同一のアセンブリ言語コードに変換します。解釈された言語はいくつかの異なるスキームを使用しましたが、基本的に言語構文は中間形式に変換され、コードを実行するための環境である「インタプリタ」で実行されました。
したがって、ある意味では、コードとマシンの間に別の「レイヤー」、つまりインタープリターがありました。また、コンピューターの場合と同様に、より多くのリソースが使用されます。通訳者は、より多くの操作を実行する必要があったため、速度が遅くなりました。
最近では、Javaのように、コンパイラーとインタープリターの両方を使用してそれらを動作させるハイブリッド言語が増えています。複雑ですが、JVMは古いインタープリターよりも高速で洗練されており、はるかに最適化されているため、単なるコンパイル済みコードに近いパフォーマンス(時間の経過)のはるかに優れた変化に対応できます。もちろん、新しいコンパイラーには、より洗練された最適化の秘訣があり、以前よりもはるかに優れたコードを生成する傾向があります。しかし、ほとんどの最適化では、ほとんどの場合(常にではありませんが)、すべての状況で常に高速であるとは限らないような、ある種のトレードオフが行われます。他のすべてと同様に、無料のものは何もないため、オプティマイザはどこかから自慢を得る必要があります(多くの場合、ランタイムCPUを節約するためにコンパイル時CPUを使用します)。
Cに戻ると、これは単純な言語であり、かなり最適化されたアセンブリにコンパイルして、ターゲットマシンで直接実行できます。Cでは、整数をインクリメントすると、CPUでのアセンブラーのステップが1つしかない可能性が高くなりますが、Javaではそれよりもはるかに多くなる可能性があります(ガベージコレクションも含まれる可能性があります)。 -)Cは、マシンに非常に近い(アセンブラが最も近い)抽象化を提供しますが、それを実現するためには、さらに多くの作業を行う必要があり、保護が不十分で、使いやすく、エラーが発生しにくいです。他のほとんどの言語は、より高い抽象化を提供し、基礎となる詳細の多くを処理しますが、高度な機能と引き換えに、実行するためにより多くのリソースを必要とします。いくつかのソリューションを一般化すると、より幅広いコンピューティングを処理する必要があります。
ポール。
++i
「add [ebp-8]、1」にコンパイルされる場合があります。Paulが言ったように、フェッチ、インクリメント、ストアがまだ行われていないことは言うまでもありませんが、これはCPUによって処理され、1つの命令にすぎません。
ホットスポット最適化、事前にコンパイルされたメタアルゴリズム、およびさまざまな形式の並列処理などの高度な最適化手法は別として、言語の基本的な速度は、一般的な操作をサポートするために必要な暗黙的な舞台裏の複雑さと強く相関します。内部ループ内で指定されます。
おそらく最も明白なのは、間接メモリ参照の有効性チェックnull
です。たとえば、配列境界に対するポインタのチェックやインデックスのチェックなどです。ほとんどの高級言語はこれらのチェックを暗黙的に実行しますが、Cは実行しません。ただし、これは必ずしもこれらの他の言語の基本的な制限ではありません -十分に賢いコンパイラは、何らかの形のループ不変コードモーションを介して、アルゴリズムの内部ループからこれらのチェックを削除できる場合があります。。
Cのより基本的な利点(および同様の程度で密接に関連するC ++)は、スタックベースのメモリ割り当てに大きく依存しています。これは、割り当て、割り当て解除、およびアクセスが本質的に高速です。C(およびC ++)では、プライマリコールスタックを使用して、プリミティブ、配列、および集約(struct
/class
)。
Cは動的に割り当てる機能を提供しますが、任意のサイズと存続期間のメモリ(いわゆる「ヒープ」を使用)を提供しますが、デフォルトではそう(代わりにスタックが使用されます)。
魅惑的なことに、他のプログラミング言語のランタイム環境内でCメモリ割り当て戦略を複製することが可能な場合があります。これはasm.jsによって実証されており、CまたはC ++で記述されたコードをJavaScriptのサブセットに変換し、ネイティブに近い速度でWebブラウザー環境で安全に実行できます。
余談ですが、CおよびC ++が他のほとんどの言語よりも速度を優れているもう1つの領域は、ネイティブの機械語命令セットとシームレスに統合する機能です。この注目すべき例は、SIMDコンパイラ組み込み関数の(コンパイラとプラットフォームに依存する)可用性です。これにより、今やほぼユビキタスな並列処理ハードウェアを利用するカスタムアルゴリズムの構築がサポートされます。レベルのレジスタ割り当てはコンパイラによって管理されます)。
一部の言語が速い理由と遅い言語についての回答をリンクで見つけました。これにより、CまたはC ++が他の言語より速い理由が明らかになることを願っています。Cよりも速い他の言語もありますが、私たちはできません。それらすべてを使用してください。いくつかの説明-
Fortranが依然として重要である大きな理由の1つは、Fortranが高速であるためです。Fortranで記述された数値計算ルーチンは、他のほとんどの言語で記述された同等のルーチンよりも高速になる傾向があります。この領域でFortranと競合している言語(CおよびC ++)は、このパフォーマンスと競合するために使用されます。
これは疑問を投げかけます:なぜですか?C ++とFortranが高速化する理由と、JavaやPythonなどの他の一般的な言語よりも優れている理由は何ですか。
解釈とコンパイルプログラミング言語を分類して定義するには、彼らが奨励するプログラミングのスタイルと提供する機能に応じて、多くの方法があります。パフォーマンスを見ると、インタープリター言語とコンパイル言語の最大の違いが1つあります。
分割は難しくありません。むしろ、スペクトルがあります。一方には、Fortran、C、およびC ++を含む従来のコンパイル言語があります。これらの言語には、プログラムのソースコードをプロセッサが使用できる実行可能な形式に変換する個別のコンパイル段階があります。
このコンパイルプロセスにはいくつかのステップがあります。ソースコードが分析され、解析されます。タイプミスやスペルミスなどの基本的なコーディングの誤りは、この時点で検出できます。解析されたコードは、メモリ内表現を生成するために使用されます。これも、誤りを検出するために使用できます。今回は、存在しない関数の呼び出しや、テキストの文字列に対する算術演算の実行など、意味上の誤りを検出します。
このインメモリ表現は、実行可能コードを生成するコードジェネレーターを駆動するために使用されます。生成されたコードのパフォーマンスを改善するためのコード最適化は、このプロセス内のさまざまな時点で実行されます。高レベルの最適化はコード表現で実行でき、低レベルの最適化はコードジェネレーターの出力で使用されます。
実際にコードを実行すると、後で発生します。コンパイルプロセス全体を使用して、実行可能なものを作成します。
反対側には通訳がいます。インタープリターにはコンパイラーと同様の解析ステージが含まれますが、これは直接実行するために使用され、プログラムはすぐに実行されます。
最も単純なインタープリターは、その言語がサポートするさまざまな機能に対応する実行可能コードを内部に持っています。そのため、数値を追加したり、文字列を結合したりするなど、特定の言語の機能を備えています。コードを解析するときに、対応する関数を検索して実行します。プログラムで作成された変数は、名前をデータにマップする何らかのルックアップテーブルに保持されます。
インタープリタースタイルの最も極端な例は、バッチファイルやシェルスクリプトのようなものです。これらの言語では、実行可能コードは多くの場合、インタープリター自体に組み込まれていませんが、個別のスタンドアロンプログラムに組み込まれています。
では、なぜこれがパフォーマンスに影響を与えるのでしょうか?一般に、間接参照の各層はパフォーマンスを低下させます。たとえば、2つの数値を加算する最も速い方法は、両方の数値をプロセッサのレジスタに入れ、プロセッサのadd命令を使用することです。これが、コンパイルされたプログラムが実行できることです。変数をレジスターに入れ、プロセッサーの命令を利用できます。しかし、解釈されたプログラムでは、同じ追加を行うには、追加する値をフェッチするために変数のテーブルで2つのルックアップが必要で、その後、追加を実行する関数を呼び出す必要があります。その関数は、コンパイルされたプログラムが実際の加算を実行するために使用するものと同じプロセッサ命令を非常によく使用する可能性がありますが、命令が実際に使用できるようになる前のすべての余分な作業により、処理が遅くなります。
詳細を知りたい場合は、ソースを確認してください
一部のC ++アルゴリズムはCよりも高速であり、他の言語でのアルゴリズムまたは設計パターンの一部の実装はCよりも高速である場合があります。
Cは高速であると人々が言った後、他の言語について話をするとき、彼らは一般的にCのパフォーマンスをベンチマークとして使用しています。
最新の最適化コンパイラーを使用すると、純粋なCプログラムがコンパイル済みの.netコードよりもはるかに高速になる可能性はほとんどありません。.netのようなフレームワークが開発者に提供する生産性の向上により、通常のCでは数週間から数か月かかっていた作業を1日で行うことができます。開発者の給与に比べてハードウェアのコストが安いことと相まって、書く方がずっと安いです。高級言語で書かれたもので、どんな遅い時にもハードウェアを投げます。
JeffとJoelがCが「本物のプログラマ」言語であることを語る理由は、Cには手持ちがないためです。独自のメモリを割り当て、そのメモリの割り当てを解除し、独自の境界チェックなどを行う必要があります。そのようなことはありません。新しいobject();として ガベージコレクション、クラス、OOP、エンティティフレームワーク、LINQ、プロパティ、属性、フィールドなどはありません。ポインタ演算のようなことや、ポインタを逆参照する方法を知っている必要があります。そして、そのことについては、ポインタが何であるかを理解してください。スタックフレームとは何か、命令ポインタは何かを知る必要があります。作業しているCPUアーキテクチャのメモリモデルを知っている必要があります。マイクロコンピュータのアーキテクチャについては多くの暗黙の理解があります(通常、、あなたが作業しているマイクロコンピュータ)Cでプログラミングするとき、C#やJavaなどでプログラミングするときは存在せず、必要もありません。これらの情報はすべて、コンパイラー(またはVM)プログラマーにオフロードされています。
それは自動と手動の違いです。高レベルの言語は抽象化であり、したがって自動化されています。C / C ++は手動で制御および処理されます。エラーチェックコードでさえ手作業になる場合があります。
CとC ++もコンパイルされた言語です。つまり、ビジネスのあらゆる場所で実行されるものはありません。これらの言語は、作業するハードウェアに合わせて微調整する必要があるため、追加のレイヤーが追加されます。C / C ++コンパイラがすべてのプラットフォームでより一般的になっているので、これは今やややおかしくなっています。プラットフォーム間でクロスコンパイルを行うことができます。それはまだどこでも実行されているわけではありません。基本的にコンパイラAにコンパイラBに対して同じコードの異なるアーキテクチャでコンパイルするように指示します。
結論として、C言語は理解しやすくしたり、理由付けしたりすることはできません。これが、システム言語と呼ばれる理由でもあります。彼らはすべてのこの高レベルの抽象化ナンセンスの前に出てきました。これが、フロントエンドWebプログラミングに使用されない理由でもあります。彼らは単にタスクに適していません。従来の言語ツールでは解決できない複雑な問題を解決することを意味します。
これが、(マイクロアーキテクチャ、ドライバ、量子物理学、AAAゲーム、オペレーティングシステム)などのクレイジーなものを手に入れる理由です。スピードと数の処理が主な領域です。
Cはネイティブにコンパイルされた低レベル言語であるため、高速です。しかし、Cは最速ではありません。再帰的フィボナッチベンチマーク錆、クリスタル、そしてニムが速くできることを示しています。
次のような多くの理由があります。