本文轉載自《如何加快C 代码的编译速度》,算是滿久以前的文章。
Heresy 自己有再根據自己的理解,重新寫一次。
首先,為什麼自己寫出來的程式碼、或是大型專案的編譯速度會很慢?其中一個原因,是因為 C 的「程式碼檔」(source file 一般都是 .cpp)和「標頭檔」(header file、一般是 .h)的架構;這個架構的基本概念是:
- 編譯器只會去編譯 source file,把它編譯成物件檔(obj 檔),最後在連結(link)起來。
- Source file 內有用到 header file,都會被讀取出來、和 source file 一起被編譯。
而在這樣的架構下,要加快編譯的速度,方法有:
-
從原始碼的角度來看
-
在 header file 裡面減少 include 其他 header file、盡量使用 forward declaration(維基百科)
因為如果 header file A.h 有 include 其他 header file 的話,編譯器要編譯有 include A.h 的 source file 的時候,就必須要把 A.h 所有 include 的 header file 全部分析過一遍、把它拿來和 source file 一起編譯。而當其中一個 header file 有做過修改的話,這個 source file 就算沒有用到修改的部分,也會需要重新編譯。
所以,可以的話,盡量把 include 這個動作,放到 source file 裡,並且只有在真正需要的時候,才去做 header file 的 include;如此一來不但可以減少實際上要編譯的程式碼數量,也可以減少需要重新編譯的頻率。
-
使用 Private Implementation,把函式的實作從 header file 拿到 source file
基本上,就是讓 header file 裡面,只有留下 class 或是 function 的定義,相關的實作都盡量不要寫在 header file、而是寫在 source file。如此一來,除了 header file 看起來會比較簡潔外,如果是定義不變、只是修改實作的內容的話,就只會修改到 source file、而不會修改到 header file;而這樣的話,其他 include 到這個 header file 的 source file,也就不需要重新編譯了~
但是另一方面,如果是要使用 inline function 或是 template 的話,就有可能沒辦法這樣做;這點就是需要取捨的了。
-
在 header file 裡面減少 include 其他 header file、盡量使用 forward declaration(維基百科)
因為如果 header file A.h 有 include 其他 header file 的話,編譯器要編譯有 include A.h 的 source file 的時候,就必須要把 A.h 所有 include 的 header file 全部分析過一遍、把它拿來和 source file 一起編譯。而當其中一個 header file 有做過修改的話,這個 source file 就算沒有用到修改的部分,也會需要重新編譯。
所以,可以的話,盡量把 include 這個動作,放到 source file 裡,並且只有在真正需要的時候,才去做 header file 的 include;如此一來不但可以減少實際上要編譯的程式碼數量,也可以減少需要重新編譯的頻率。
-
-
綜合技巧
-
減少指定的 Include 資料夾
很單純的一個狀況。編譯器會在指定的 Include 資料夾(Additional Include Directories)裡面、去找所需要的 header file;當所給的資料夾過多的時候,編譯器有可能會花更多的時間、來尋找所要求的 header 檔到底在哪裡。所以避免告訴編譯器不需要的 Include 資料夾,也可以加快編譯時的前處理速度。
-
使用預先編譯好的 Header File
-
Unity Builds
「Unity Builds」的方法,基本上就是用一個 source file(例如 all.cpp)把所有的 source file include 進來,然後不要個別編譯單獨 source file,只去編譯那個包含所有 source file 的檔案(all.cpp)。如此一來,可以減少產生出來的 object file 的數量、以減少最後 linking 時的檔案存取的負擔,並大幅加快處理的速度。(參考《The Magic of Unity Builds》)
不過這個做法要注意的是,這個做法對編譯器來說,其實就是變成是在編譯一個超大的檔案;而如果本來在不同的 source file 裡面,有同名的全域變數、函式的話,是會產生衝突了~另外,在這情況下,修改單一 source file 也會造成所有的 source file 的內容都需要重新編譯,所以應該也是比較適用於不常修改的 source file。
-
-
硬體資源
-
平行編譯
使用多核心處理器,然後編譯環境設定好,就可以同時編譯多個檔案,減少整個專案所需的建置時間。
以 Microsoft Visula C 來說,在可能的情況下,預設應該就會使用所有的 CPU 核心來做平行編譯;而 Linux 的 GNU Make 系統,一般應該是透過「-j」這個參數,來指定要分成幾個工作來平行進行。 -
更好的磁碟
編譯的速度慢、很有可能是因為檔案存取的速度造成的瓶頸;所以除了盡量減少磁碟的存取外,也可以透過更換儲存裝置、來加快編譯時的檔案存取速度,例如使用更好的硬碟、RAID、或是 SSD。
-
分散式編譯
用多台電腦來進行編譯。個人覺得除非編譯時間真的久到一定程度、而且手邊可用的機器又夠多,不然用到機會不大就是了。
另外,該文還有介紹一本《大型 C 軟體設計 (Large-Scale C Software Design)》(連結),或許也是可以參考看看的。
-
謝謝您提到”大型C 軟體設計”這本書
我是該書的作者
誠心推薦您
Simon