首頁 >後端開發 >C++ >C和C真的那麼快嗎?

C和C真的那麼快嗎?

Susan Sarandon
Susan Sarandon原創
2024-12-07 09:25:13951瀏覽

C and C   are really so fast?

我從事程式設計的那段時間,聽說C和C是速度標準。最快中的最快,直接編譯為彙編程式碼,速度上沒有任何東西可以與 C 或 C 競爭。而且,似乎沒有人挑戰這個共同信念。

運算效能

顯然,數字算術運算在 C 中的運行速度必須比在任何其他語言中快得多。但他們有嗎?
前段時間,我決定為許多不同的語言編寫一組簡單的基準測試,看看速度有多大差異。
想法很簡單:從零開始,使用直接計算來找到十億個整數的總和。有些編譯器(例如rustc)用公式表達式取代這個簡單的循環,當然,公式表達式將在恆定的時間內進行計算。用這樣的編譯器來避免這種情況。我在數位成本運算中使用了類似的方法,例如位元或
得到結果後,我非常驚訝。我的世界觀發生了翻天覆地的變化,我不得不重新考慮我所知道的關於程式語言速度的一切。
您可以在下表中看到我的結果:

Linux 64 位元1.1 GHz CPU,4GB RAM

語言 編譯器/版本/參數 時間 標題> Rust(
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
位元或而不是 ) 帶有 -O3 的 rustc 1.75.0 167 毫秒 C 帶有-O3的gcc 11.4.0 335 毫秒 NASM 05.15 339 毫秒 去 1.18.1 340 毫秒 Java 17.0.13 345 毫秒 通用 Lisp SBCL 2.1.11 1秒 Python 3 pypy 3.8.13 1.6 秒 Clojure 1.10.2 9秒 Python 3 cpython 3.10.12 26 秒 紅寶石 3.0.2p107 38秒 表>

您可以在這裡找到所有測試來源:
https://github.com/Taqmuraz/speed-table

因此,如我們所看到的,C 並不比 Java 快很多,差異約為 3%。此外,我們還發現其他編譯語言的算術運算效能與 C 非常接近(Rust 甚至更快)。使用 JIT 編譯器 編譯的動態語言顯示出更糟糕的結果 - 主要是因為算術運算被包裝到動態分派的函數中。
解釋型動態語言沒有 JIT 編譯器表現出最差的效能,這並不奇怪。

記憶體分配效能

在那次慘敗之後,C 粉絲會說 C 中的記憶體分配要快得多,因為你直接從系統分配它,而不是要求 GC
現在和之後我將使用 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
1.1 GHz 2 核,4 GB RAM 在單執行緒上執行測試。 結果格式:「Xms-Yms ~Z ms」表示測試從X到Y毫秒,平均Z毫秒 表>

分配整數數組

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

為 Person 類別的實例指派一個整數欄位。

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

在那裡我總共測試了四種語言:C、C、Java 和 Lisp。而且,帶有 GC 的語言總是顯示出更好的結果,儘管我對它們的測試比 C 和 C 嚴格得多。例如,在 Java 中,我透過虛擬函數呼叫分配內存,因此它可能不會被靜態優化,而在 Lisp 中,我正在檢查分配數組的第一個元素,因此編譯器不會跳過分配呼叫。

釋放記憶體

C 粉絲仍然有動力去保護他們的信仰,所以,他們說「是的,你分配記憶體確實更快,但你必須在之後釋放它!」。
真的。而且,突然之間,GC 釋放記憶體的速度比 C 還要快。但是如何呢?想像一下,我們從 GC 進行了 100 萬次分配,但後來我們的程式中只引用了 1000 個物件。而且,比方說,這些物件分佈在所有這麼長的記憶體範圍內。 GC 進行堆疊跟踪,找到那 1000 個「活動」對象,將它們移動到上一代堆疊峰值,並將堆疊峰值指針放在最後一個物件之後。就這樣。
所以,無論你分配了多少物件GC的工作時間取決於之後你保留了多少物件
而且,與此相反,在 C 中,您必須手動釋放所有分配的內存,因此,如果您分配內存 100 萬次,您也必須進行 100 萬次釋放調用(否則將會出現內存洩漏)。這表示GCO(1)-O(n) 與C 的O(n) 或更差,其中n是之前發生的分配次數。

概括

所以,我想鞏固垃圾收集語言對 C 和 C 的勝利。這是總結表:

要求 帶有 GC 的語言 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
JIT快速 快 分配記憶體 快O(1) 慢 釋放記憶體 快速O(1)最好情況,O(n)最壞情況 O(n) 或更慢 記憶體安全 是 沒有 表>

現在我們可能會看到-垃圾收集並不是一種必要的罪惡,而是我們唯一希望擁有的最好的東西。它為我們提供了安全性性能兩者

向C致敬

雖然 C 在我的測驗中確實顯示出較差的結果,但它仍然是一門重要的語言,並且有自己的應用領域。我的文章並不是為了拒絕或刪除 C。 C也不錯,只是沒有人們想像的那麼優越。許多好的專案失敗只是因為有些人決定使用 C 而不是 Java,例如,因為他們被告知 C 更快,而 Java 由於垃圾收集而慢得令人難以置信。當我們編寫非常小且簡單的程式時,C 很好。但是,我絕對不會建議用 C 來寫複雜的程式或遊戲。

C 不同

C 不簡單,不靈活,文法超載,規格太多複雜。使用 C 編程,你不會實現自己的想法,但 90% 的時間都會與編譯器和記憶體錯誤作鬥爭。
這篇文章的目的是拒絕C語言,因為速度和效能只是人們在軟體開發中使用這種語言的藉口。使用 C ,您要付出時間、程序表現和心理健康的代價。所以,當你在 C 和任何其他語言之間做出選擇時,我希望你選擇最後一種。

以上是C和C真的那麼快嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn