C和C真的那麼快嗎?
我從事程式設計的那段時間,聽說C和C是速度標準。最快中的最快,直接編譯為彙編程式碼,速度上沒有任何東西可以與 C 或 C 競爭。而且,似乎沒有人挑戰這個共同信念。
運算效能
顯然,數字算術運算在 C 中的運行速度必須比在任何其他語言中快得多。但他們有嗎?
前段時間,我決定為許多不同的語言編寫一組簡單的基準測試,看看速度有多大差異。
想法很簡單:從零開始,使用直接計算來找到十億個整數的總和。有些編譯器(例如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 粉絲會說 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 |
分配整數數組
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 萬次釋放調用(否則將會出現內存洩漏)。這表示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。 C也不錯,只是沒有人們想像的那麼優越。許多好的專案失敗只是因為有些人決定使用 C 而不是 Java,例如,因為他們被告知 C 更快,而 Java 由於垃圾收集而慢得令人難以置信。當我們編寫非常小且簡單的程式時,C 很好。但是,我絕對不會建議用 C 來寫複雜的程式或遊戲。
C 不同
C 不簡單,不靈活,文法超載,規格太多複雜。使用 C 編程,你不會實現自己的想法,但 90% 的時間都會與編譯器和記憶體錯誤作鬥爭。
這篇文章的目的是拒絕C語言,因為速度和效能只是人們在軟體開發中使用這種語言的藉口。使用 C ,您要付出時間、程序表現和心理健康的代價。所以,當你在 C 和任何其他語言之間做出選擇時,我希望你選擇最後一種。
以上是C和C真的那麼快嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

C語言數據結構:樹和圖的數據表示與操作樹是一個層次結構的數據結構由節點組成,每個節點包含一個數據元素和指向其子節點的指針二叉樹是一種特殊類型的樹,其中每個節點最多有兩個子節點數據表示structTreeNode{intdata;structTreeNode*left;structTreeNode*right;};操作創建樹遍歷樹(先序、中序、後序)搜索樹插入節點刪除節點圖是一個集合的數據結構,其中的元素是頂點,它們通過邊連接在一起邊可以是帶權或無權的數據表示鄰

文件操作難題的真相:文件打開失敗:權限不足、路徑錯誤、文件被佔用。數據寫入失敗:緩衝區已滿、文件不可寫、磁盤空間不足。其他常見問題:文件遍歷緩慢、文本文件編碼不正確、二進製文件讀取錯誤。

C語言函數是代碼模塊化和程序搭建的基礎。它們由聲明(函數頭)和定義(函數體)組成。 C語言默認使用值傳遞參數,但也可使用地址傳遞修改外部變量。函數可以有返回值或無返回值,返回值類型必須與聲明一致。函數命名應清晰易懂,使用駝峰或下劃線命名法。遵循單一職責原則,保持函數簡潔性,以提高可維護性和可讀性。

C語言函數名定義包括:返回值類型、函數名、參數列表和函數體。函數名應清晰、簡潔、統一風格,避免與關鍵字衝突。函數名具有作用域,可在聲明後使用。函數指針允許將函數作為參數傳遞或賦值。常見錯誤包括命名衝突、參數類型不匹配和未聲明的函數。性能優化重點在函數設計和實現上,而清晰、易讀的代碼至關重要。

C語言函數是可重複利用的代碼塊,它接收輸入,執行操作,返回結果,可將代碼模塊化提高可複用性,降低複雜度。函數內部機制包含參數傳遞、函數執行、返回值,整個過程涉及優化如函數內聯。編寫好的函數遵循單一職責原則、參數數量少、命名規範、錯誤處理。指針與函數結合能實現更強大的功能,如修改外部變量值。函數指針將函數作為參數傳遞或存儲地址,用於實現動態調用函數。理解函數特性和技巧是編寫高效、可維護、易理解的C語言程序的關鍵。

C35 的計算本質上是組合數學,代表從 5 個元素中選擇 3 個的組合數,其計算公式為 C53 = 5! / (3! * 2!),可通過循環避免直接計算階乘以提高效率和避免溢出。另外,理解組合的本質和掌握高效的計算方法對於解決概率統計、密碼學、算法設計等領域的許多問題至關重要。

算法是解決問題的指令集,其執行速度和內存佔用各不相同。編程中,許多算法都基於數據搜索和排序。本文將介紹幾種數據檢索和排序算法。線性搜索假設有一個數組[20,500,10,5,100,1,50],需要查找數字50。線性搜索算法會逐個檢查數組中的每個元素,直到找到目標值或遍歷完整個數組。算法流程圖如下:線性搜索的偽代碼如下:檢查每個元素:如果找到目標值:返回true返回falseC語言實現:#include#includeintmain(void){i

C#和C 的歷史與演變各有特色,未來前景也不同。 1.C 由BjarneStroustrup在1983年發明,旨在將面向對象編程引入C語言,其演變歷程包括多次標準化,如C 11引入auto關鍵字和lambda表達式,C 20引入概念和協程,未來將專注於性能和系統級編程。 2.C#由微軟在2000年發布,結合C 和Java的優點,其演變注重簡潔性和生產力,如C#2.0引入泛型,C#5.0引入異步編程,未來將專注於開發者的生產力和雲計算。
