編譯器 - dev c++教學




為什麼C++編譯需要這麼長時間? (10)

與C#和Java相比,編譯C ++文件需要很長時間。 編譯C ++文件比運行普通大小的Python腳本要花費更長的時間。 我目前正在使用VC ++,但對於任何編譯器都是一樣的。 為什麼是這樣?

我能想到的兩個原因是加載頭文件和運行預處理器,但這似乎並不能解釋為什麼它需要這麼長時間。


C ++被編譯成機器碼。 所以你有預處理器,編譯器,優化器,最後是彙編器,所有這些都必須運行。

Java和C#被編譯為字節碼/ IL,並且Java虛擬機/ .NET Framework在執行之前執行(或JIT編譯為機器代碼)。

Python是一種解釋型語言,也被編譯為字節碼。

我確信還有其他原因,但一般來說,不需要編譯為本地機器語言可以節省時間。


構建C / C ++:究竟發生了什麼,為什麼需要這麼長時間

軟件開發時間的相當大部分不用於編寫,運行,調試甚至設計代碼,而是等待編譯完成。 為了讓事情變得更快,我們首先必須了解編譯C / C ++軟件時發生了什麼。 步驟大致如下:

  • 組態
  • 構建工具啟動
  • 依賴性檢查
  • 彙編
  • 鏈接

現在我們將更詳細地審視每一步,重點關注如何使它們變得更快。

組態

這是開始構建時的第一步。 通常意味著運行配置腳本或CMake,Gyp,SCons或其他工具。 對於非常大的基於Autotools的配置腳本,這可能需要一秒到幾分鐘的時間。

這一步發生得比較少。 只需在更改配置或更改構建配置時運行即可。 在改變構建系統的情況下,為了加快這一步驟,並沒有太多的工作要做。

構建工具啟動

當您運行make或單擊IDE上的構建圖標(通常是make的別名)時,會發生這種情況。 構建工具二進製文件啟動並讀取其配置文件以及構建配置,這通常是相同的事情。

根據構建的複雜性和大小,這可能需要幾秒到幾秒的時間。 本身並不會那麼糟糕。 不幸的是,大多數基於make的構建系統在每個構建中都會導致調用數十到數百次。 通常這是由make的遞歸使用引起的(這是不好的)。

應該指出的是,Make非常慢的原因並不是一個執行錯誤。 Makefiles的語法有一些怪癖,使得一個非常快速的實現幾乎不可能。 與下一步相結合時,這個問題更加明顯。

依賴性檢查

構建工具讀取其配置後,必須確定哪些文件已更改以及哪些文件需要重新編譯。 配置文件包含描述構建依賴關係的有向無環圖。 此圖通常在配置步驟中構建。 構建工具啟動時間和依賴關係掃描程序在每個構建版本上運行。 它們的組合運行時間決定了編輯 - 編譯 - 調試週期的下限。 對於小型項目來說,這個時間通常是幾秒鐘左右。 這是可以容忍的。 有替代品。 其中最快的是由Google工程師為Chromium製作的Ninja。 如果你正在使用CMake或Gyp來構建,只需切換到他們的忍者後端。 您不必在構建文件本身中更改任何內容,只需享受提速。 儘管忍者在大多數發行版上都沒有打包,所以你可能需要自己安裝它。

彙編

此時我們最終調用編譯器。 切割一些角落,這裡是採取的大致步驟。

  • 合併包括
  • 解析代碼
  • 代碼生成/優化

與流行的觀點相反,編譯C ++實際上並不是那麼慢。 STL很慢,大多數用於編譯C ++的構建工具都很慢。 但是,有更快的工具和方法可以緩解語言的慢速部分。

使用它們需要一點肘部潤滑脂,但好處是不可否認的。 更快的構建時間帶來更快樂的開發人員,更敏捷,最終更好的代碼。


任何編譯器的放緩都不一定相同。

我沒有使用Delphi或Kylix,但回到MS-DOS時代,Turbo Pascal程序幾乎可以立即編譯,而等效的Turbo C ++程序只會抓取。

兩個主要區別是一個非常強大的模塊系統和允許單次編譯的語法。

對於C ++編譯器開發人員來說,編譯速度當然不是優先考慮的事情,但C / C ++語法中也存在一些固有的複雜性,這使得它更難處理。 (我不是C方面的專家,但Walter Bright是,並且在構建各種商業C / C ++編譯器之後,他創建了D語言。 他的一個變化是強制使用上下文無關語法來使語言更易於解析。)

另外,您會注意到,通常會設置Makefiles,以便每個文件都在C中分別編譯,因此如果10個源文件全部使用相同的包含文件,那麼包含文件將被處理10次。


你得到的折衷是程序運行速度更快。 在開發過程中,這可能會讓你感到很冷淡,但一旦開發完成,這可能會很重要,並且該程序僅由用戶運行。


在較大的C ++項目中減少編譯時間的簡單方法是創建一個* .cpp包含文件,其中包含項目中的所有cpp文件並編譯該文件。 這將頭部爆炸問題減少到一次。 這樣做的好處是編譯錯誤仍然會引用正確的文件。

例如,假設你有a.cpp,b.cpp和c.cpp ..創建一個文件:everything.cpp:

#include "a.cpp"
#include "b.cpp"
#include "c.cpp"

然後通過製作everything.cpp來編譯該項目


大多數答案都有點不清楚,提到C#總是會運行得慢一些,因為在編譯時只執行一次C ++執行的操作的代價,這種性能成本也會受到運行時依賴關係的影響(需要加載更多的東西運行),更不用說C#程序總是會有更大的內存佔用,所有這些導致性能與硬件的可用性更密切相關。 其他解釋或依賴虛擬機的語言也是如此。


最大的問題是:

1)無限標題重新分析。 已經提到。 緩解(像#pragma一次)通常只對每個編譯單元有效,而不是每個構建。

2)工具鏈經常被分成多個二進製文件(在極端情況下為make,預處理器,編譯器,彙編器,歸檔器,impdef,鏈接器和dlltool),每次調用都必須重新初始化並重新加載所有狀態編譯器,彙編程序)或每一對文件(存檔器,鏈接器和dlltool)。

另請參閱comp.compilers上的這個討論: http://compilers.iecc.com/comparch/article/03-11-078 ://compilers.iecc.com/comparch/article/03-11-078特別是這一個:

http://compilers.iecc.com/comparch/article/02-07-128

請注意,comp.compilers的主持人John似乎同意這一點,並且這意味著如果一個工具鏈完全集成並實現預編譯頭文件,那麼也應該可以為C實現類似的速度。 許多商業C編譯器在一定程度上做到了這一點。

請注意,將所有東西都分解成單獨二進製文件的Unix模型是Windows的一種最差情況模型(創建過程緩慢)。 比較Windows和* nix之間的GCC編譯時間是非常明顯的,特別是如果make / configure系統也調用一些程序來獲取信息。


有兩個我能想到的問題可能會影響C ++程序編譯的速度。

可能的問題#1 - 編譯頭文件:(這可能已經或可能未被其他答案或評論解決。)Microsoft Visual C ++(AKA VC ++)支持預編譯頭文件,我強烈建議。 當您創建一個新項目並選擇正在製作的程序類型時,屏幕上應該會出現設置嚮導窗口。 如果您點擊底部的“下一步>”按鈕,該窗口將帶您進入具有多個功能列表的頁面; 確保選中“預編譯頭”選項旁邊的框。 (注意:這是我在C ++中使用Win32控制台應用程序的經驗,但對於C ++中的各種程序可能不是這種情況。)

可能的問題#2 - 編譯的位置:今年夏天,我參加了一門編程課程,我們必須將所有項目存儲在8GB閃存驅動器上,因為我們使用的實驗室中的計算機在午夜時分每天晚上都被擦除,這會抹去我們所有的工作。 如果您為了便攜性/安全性等目的而編譯到外部存儲設備,則需要很長時間 (即使使用上面提到的預編譯頭文件)來編譯程序,特別是如果它相當大程序。 在這種情況下,我的建議是在您使用的計算機的硬盤驅動器上創建和編譯程序,並且無論出於何種原因,只要您想/需要停止處理項目,就將它們轉移到您的外部存儲設備,然後單擊“安全刪除硬件並彈出介質”圖標,該圖標應該顯示為一個帶有白色複選標記的小綠色圓圈後面的小型閃存驅動器,以斷開連接。

我希望這可以幫助你; 讓我知道如果它! :)


編譯語言總是需要比解釋語言更大的初始開銷。 另外,也許你沒有很好地構建你的C ++代碼。 例如:

#include "BigClass.h"

class SmallClass
{
   BigClass m_bigClass;
}

編譯比以下更慢:

class BigClass;

class SmallClass
{
   BigClass* m_bigClass;
}

解析和代碼生成實際上是相當快的。 真正的問題是打開和關閉文件。 請記住,即使使用包含警衛,編譯器仍然打開.H文件,並讀取每行(然後忽略它)。

一位朋友曾經(在工作中感到無聊)採取了公司的應用程序,並將所有源代碼和頭文件都放入一個大文件中。 編譯時間從3小時減少到7分鐘。





compilation