私がずっとプログラミングに取り組んでいると、速度の標準は C と C だと聞きます。最速中の最速で、アセンブリ コードに直接コンパイルされ、速度において C や C に匹敵するものはありません。そして、その一般的な信念に異議を唱える人は誰もいないようです。
数値を使った算術演算は、明らかに、C では他の言語よりも大幅に高速に動作する必要があります。でもそうですか?
少し前に、私は速度の実際の違いがどれほど大きいかを確認するために、さまざまな言語用の一連の簡単なベンチマークを作成することにしました。
アイデアは単純でした。単純な計算を使用して、ゼロから始まる 10 億の整数の合計を求めるというものです。一部のコンパイラ (rustc など) は、このような単純なサイクルを数式に置き換えます。もちろん、これは一定時間で評価されます。そのようなコンパイラでそれを回避するには。 ビット単位またはなど、数値を使用したコスト演算でも同様の操作を使用しました。
結果が出た後、とても驚きました。私の世界観はひっくり返り、プログラミング言語の速度について自分が知っていたすべてを再考する必要がありました。
以下の表に私の結果が表示されます:
Linux 64 ビット、1.1 GHz CPU、4GB RAM
Language | compiler/version/args | time |
---|---|---|
Rust (bitwise or instead of ) | rustc 1.75.0 with -O3 | 167 ms |
C | gcc 11.4.0 with -O3 | 335 ms |
NASM | 2.15.05 | 339 ms |
Go | 1.18.1 | 340 ms |
Java | 17.0.13 | 345 ms |
Common Lisp | SBCL 2.1.11 | 1 sec |
Python 3 | pypy 3.8.13 | 1.6 sec |
Clojure | 1.10.2 | 9 sec |
Python 3 | cpython 3.10.12 | 26 sec |
Ruby | 3.0.2p107 | 38 sec |
ここにあるすべてのテスト ソース:
https://github.com/Taqmuraz/speed-table
ご覧のとおり、C は Java よりもそれほど高速ではなく、その差は約 3% です。また、他のコンパイル言語は算術演算のパフォーマンスが C に非常に近いことがわかります (Rust はさらに高速です)。 JIT コンパイラ でコンパイルされた動的言語は、より悪い結果を示します。主な原因は、算術演算がそこで動的にディスパッチされる関数にラップされているためです。
JIT コンパイラなしで解釈された動的言語は最悪のパフォーマンスを示しますが、驚くべきことではありません。
あの惨敗の後、C ファンは、GC を要求せずにシステムから直接メモリを割り当てるため、C でのメモリ割り当てが非常に高速であると言うでしょう。
今後も、コンテキストに応じて、GC という用語を ガベージ コレクター と マネージド ヒープ の両方として使用します。
では、なぜ人々は GC がとても遅いと考えるのでしょうか?実際、GC には事前に割り当てられたメモリがあり、割り当ては単にポインタを右に移動するだけです。ほとんどの場合、GC は、C の memset と同様に、システム コールを使用して割り当てられたメモリをゼロで埋めるため、一定の時間 がかかります。 C でのメモリ割り当てには、システムとすでに割り当てられているメモリに依存するため、不定の時間がかかります。
しかし、この知識を考慮しても、Java からそれほど良い結果は期待できませんでした。これは次の表に示すとおりです。
1.1 GHz 2 cores, 4 GB RAM |
Running tests on single thread. |
Result format : "Xms-Yms ~Z ms" means tests took from X to Y milliseconds, and Z milliseconds in average |
integers array size | times | Java 17.0.13 new[] | C gcc 11.4.0 malloc | Common Lisp SBCL 2.1.11 make-array |
---|---|---|---|---|
16 | 10000 | 0-1ms, ~0.9ms | 1-2ms, ~1.2ms | 0-4ms, ~0.73ms |
32 | 10000 | 1-3ms, ~1.7ms | 1-3ms, ~1.7ms | 0-8ms, ~2.ms |
1024 | 10000 | 6-26ms, ~12ms | 21-46ms, ~26ms | 12-40ms, ~7ms |
2048 | 10000 | 9-53ms, ~22ms | 24-52ms, ~28ms | 12-40ms, ~19ms |
16 | 100000 | 0-9ms, ~2ms | 6-23ms, ~9ms | 4-24ms, ~7ms |
32 | 100000 | 0-14ms, ~3ms | 10-15ms, ~11ms | 3-8ms, ~7ms |
1024 | 100000 | 0-113ms, ~16ms | 234-1156ms, ~654ms | 147-183ms, ~155ms |
2048 | 100000 | 0-223ms, ~26ms | 216-1376ms, ~568ms | 299-339ms, ~307ms |
how many instances | Java 17.0.3 new Person(n) | C g 11.4.0 new Person(n) |
---|---|---|
100000 | 0-6ms, ~1.3ms | 4-8ms, ~5ms |
1 million | 0-11ms, ~2ms | 43-69ms, ~47ms |
1 billion | 22-50ms, ~28ms | process terminated |
ここにあるすべてのテスト ソース:
https://github.com/Taqmuraz/alloc-table
そこで私は合計 4 つの言語 (C、C、Java、Lisp) をテストしました。また、 GC を使用した言語は、 C や C よりもはるかに厳密にテストしましたが、常に良い結果を示します。たとえば、Java では仮想関数呼び出しを通じてメモリを割り当てているため、静的に最適化されない可能性があります。また、Lisp では割り当てられた配列の最初の要素をチェックしているため、コンパイラは割り当て呼び出しをスキップしません。
C ファンは依然として自分たちの信念を守ろうとする動機を持っているため、「はい、メモリの割り当ては速くなりますが、その後は解放する必要があります。」と言います。
真実。そして突然、GC は C よりも速くメモリを解放します。しかし、どのようにしてメモリを解放するのでしょうか? GC から 100 万回の割り当てを行ったが、その後、プログラム内で参照されるオブジェクトが 1000 個だけになったと想像してください。そして、それらのオブジェクトがその長いメモリ全体に分散されているとします。 GC はスタック トレースを実行し、これら 1000 個の「生きている」オブジェクトを見つけて、それらを前の世代のヒープ ピークに移動し、最後のオブジェクトの後にヒープ ピーク ポインタを置きます。以上です。
したがって、割り当てるオブジェクトの数に関係なく、GCの作業時間は、後に保持するオブジェクトの数によって決まります。
そして、それとは逆に、C では割り当てられたメモリをすべて手動で解放する必要があるため、メモリを 100 万回割り当てた場合は、同様に 100 万回の解放呼び出しを行う必要があります (そうしないとメモリ リークが発生します)。つまり、GC の O(1)-O(n) と C の O(n) またはそれより悪い (n) に対するものです。は以前に発生した割り当ての数です。
そこで、C および C に対するガベージ コレクション言語の勝利を確固たるものにしたいと考えています。概要表は次のとおりです:
demands | languages with GC | C/C |
---|---|---|
arithmetic | fast with JIT | fast |
allocating memory | fast O(1) | slow |
releasing memory | fast O(1) best case, O(n) worst case | O(n) or slower |
memory safe | yes | no |
これで、ガベージ コレクションは必要悪ではなく、私たちが望むだけの最高のものであることがわかるかもしれません。それは私たちに安全性とパフォーマンス両方をもたらします。
C は私のテストでは悪い結果を示しましたが、依然として重要な言語であり、独自の応用分野があります。私の記事はCの拒絶や抹殺を目的としたものではありません。 C は悪くはありませんが、人々が思っているほど優れているわけではありません。多くの優れたプロジェクトが崩壊したのは、一部の人が Java の代わりに C を使用することを決めたという理由だけです。たとえば、C の方がはるかに高速で、Java はガベージ コレクションのせいで信じられないほど遅いと言われてきたためです。非常に小さく単純なプログラムを作成する場合には、C が適しています。ただし、C を使用して複雑なプログラムやゲームを作成することは決してお勧めしません。
C は単純ではなく、柔軟性がなく、構文が多重定義されており、仕様が複雑すぎます。 C でプログラミングすると、自分のアイデアを実装することはできませんが、90% の確率でコンパイラ エラーやメモリ エラーと戦うことになります。
この記事は C を拒否することを目指しています。速度とパフォーマンスは人々がソフトウェア開発でこの言語を使用するための言い訳にすぎないからです。 C を使用すると、時間、プログラムのパフォーマンス、精神的健康で対価を支払うことになります。したがって、C と他の言語のどちらかを選択する場合は、最後の言語を選択してください。
以上がCとCは本当に速いですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。