C語言會被解釋成彙編語言,為什麼C會比彙編慢呢?
C語言在編譯時會被編譯成彙編語言,再講彙編語言編譯成機器指令,最後生成可執行文件,那為什麼C語言卻會被認為比彙編語言慢呢?
看了下前面幾個回答雖然說不能算錯但是感覺並不能有效的解答這個問題,咱來試著回答一下吧。
首先,asm的執行速度本身可以在宏觀上認為是一樣的——這個一樣是指兩段一樣的代碼,在同一台電腦上運行,那麼運行時間應該是在一個可接受的誤差範圍內相同。
那麼造成c/c++編譯後的代碼比asm要慢的理由在哪裡呢?我個人認為在於其為代碼健壯性和可維護性所產生的冗餘而拖慢了「執行速度」,也就是說,在asm和c/c++裡面執行同一個邏輯過程的代碼,在由c/c++翻譯到asm的過程中,由於程序員為了代碼的健壯性而增加的一些必要的附加邏輯,以及因為使用c/c++在增強了可維護性的同時所增加的額外邏輯,使得c/c++編譯後的asm源文件要比單純執行這個邏輯過程的asm代碼多了許多額外的東西,而這些必然要拖慢邏輯。
舉一個例子
在c++的函數調用約定中有所謂的__cdecl和__fastcall兩種(win32平台),那麼__cdecl代表了正常的由右向左壓棧,caller清棧,而__fastcall將前兩個DWORD直接放在了ecx和edx上而不是壓棧。很顯然,假如是直接編寫asm程序的話,在遇到可以利用ecx和edx直接傳送參數的情況下肯定會毫不猶豫的直接使用,甚至在必要的時候可以利用其它寄存器來直接傳遞參數,這顯然是c/c++函數調用所無法比擬的優勢
再來一個例子
在c/c++中,用引用的方式傳遞參數可以實現對通過改變形參同時改變實參,以此實現許多單純靠返回值無法達成的功能。但是對於默認變數類型,如int, float等的引用傳遞時,在函數棧中壓棧的時候會有一次額外的偏移地址壓棧,也就導致了運行階段一次額外的定址操作,這就使得引用傳遞的執行速度要明顯慢於值傳遞,但是c/c++單返回值的特性決定了在某些特定場合必須使用這種方法進行傳遞。而asm里並沒有返回值的概念,完全可以通過靈活的運用寄存器完成多返回值的實現,從而大大加快運行速度。
再加上前面其他人的回答中所提到的,編譯器自動編譯和優化的代碼畢竟不是程序員手寫的代碼,在要求代碼符合編譯規範的同時必然也會同時產生一些不那麼靈活的刻板的東西,這些地方也多多少少會引入冗餘的操作。
所以大概的總結來說,慢的並不是c/c++本身,而是c/c++相比於asm多了許多額外的邏輯過程。然而也正因為有了這些邏輯過程,c/c++為源代碼的程序才會更易於掌控和進行大規模開發。按照曾經看到的過的某前輩的說法就是,asm就是一台功率max的跑車,然而這個跑車並沒有任何控制,c語言的作用就是給這台跑車加上了方向盤和剎車,雖然直線速度變慢了,但起碼這輛車能正確的跑彎道和安全的停下來了。很多人有個錯覺:把高級語言代碼等價翻譯成較低級語言代碼可以提升性能。
其實本質在於,用低級語言一般可以做到高級語言做不到的一些事情,其中(可能)包括一些可以提升性能的事情。一般而言 沒覺得會快。必須要用彙編的場合往往是一些特殊控制指令 在C裡面沒有對應的語句 比如清個緩存 切換個模式之類的。而有些對性能要求很高的場合 仔細調一下編譯器的選項和源代碼 一般也能調出來
你讓寫彙編的做個寄存器分配 指令調度 立馬跪了見過好多手寫彙編的 仔細看是有bug的 只是大部分時候運氣好 沒暴露出來而已裡面經驗豐富的C/ASM程序員手寫的彙編能超越現代編譯器生成的-O3代碼...
然而事實上多數手寫彙編的人寫出來的彙編代碼效率並不能與現代編譯器生成的彙編代碼比。不說具體情況就"比較速度"就是耍流氓。
那不廢話,編譯器再怎麼優化,優化方案也是人類事先設定的。寫彙編可以直接用上去。如果誰寫出來的彙編比C慢,說明你彙編技巧有待改進,可以打開C程序的反彙編,向編譯器學幾招。
因為同一個需求, 用c實現並解釋為彙編語言的代碼和直接用彙編實現的代碼並不是同一段代碼。
你可以去看看同樣輸出hello world,彙編代碼和c語言反彙編以後的代碼。兩個的指令數量差了10多倍。即使是o3之後的代碼。
雖然都是給eax寄存器賦值,int 80等操作。可是問題出在哪兒呢?看代碼可以知道,printf提供了更強大的功能,為了方便實用,加入了很多比如類型格式化之類的功能。舉個不恰當的栗子:a = 1; //mov mem_a, 1;
b = 2; //mov mem_b, 2;
a += b; //load r0, mem_a;//load r1, mem_b;//add r0, r1;//store r0, mema;和下面的直接寫彙編比較的話:mov r0, 1;mov r1, 2;add r0, r1;store r0, mem_a;
哪個更快,當然了,這個栗子也許不怎麼恰當。。。有點自動檔和手動擋區別的意思。高手開手動擋比自動檔有效率、有樂趣,菜鳥開自動檔比手動擋省心
1.C生成的還是彙編,是編譯器自動生成的,當然某些地方會不如人為優化。2.編輯器生成的彙編會有冗餘代碼,浪費了一點性能。3.編譯器考慮到通用性,不會調用某些處理器的黑科技指令,而用彙編可以調用。4.要寫高性能代碼不如直接用C和內嵌彙編,組合起來才強大,直接用彙編寫代碼可不好玩。
不考慮調用的庫的差異而單說針對某個邏輯編譯後的結果的話,一般而言問題出在函數調用上。C的函數調用大多數編譯器為了保險,會在調用開始時執行一條串指令,在前面寫上一堆0xCC(int 3; 斷點中斷)用來避免局部變數溢出。所以你每調用一次函數,基本都會導致平白無故地循環了半天填充0xCC。而彙編一般人寫基本上壓一下寄存器保護下現場也就完了,很少有人去干這種級別的保護,所以調用時能省下不少時間。但是對於除了函數調用以外絕大多數邏輯來講,只要你的彙編實現思路和C思路一樣,那麼效率差異很小。可以說為數不多的剩下的問題也就在於C中對於類型轉換的處理了。C中類型轉換很常見(比如浮點數與整數運算你要先把浮點數壓到FPU棧(也有叫寄存器的)里,然後把整數轉換為浮點數,再壓到棧里,最後運算),實現方法基本上是用擴展指令(當然有些編譯器已經機智到可以用後面的ptr大法了),而你用彙編肯定直接xx ptr xx,不會有人蛋疼到還寫個擴展指令來轉類型。但說句實在話,之所以很多人覺得彙編比C快,其實並非上述原因,而是因為幾乎所有人都很難拿彙編寫出什麼複雜的東西,它實在是太麻煩了,只有在人們能寫出的範圍內,彙編因為沒有C那麼多準備工作才讓人感到快。
如果僅是用到cpu與內存,那快與慢,較大程度取決於cpu cache miss與否,跟哪種語言無關。
你說的「慢」準確的說是執行效率,如果是開發效率的話高級語言更快。
推薦閱讀:
※如何學習開源代碼?有什麼好的書籍可以引導初學者學習?
※用 C++ 實現大整數的加減,思路是什麼?
※為什麼計算機語言中的變數名都不能以數字開頭呢?
※對於程序員來說,哪些網站代碼比較多比較全,問題解決比較快?
※go有哪些快速開發的web框架?