借助 CVS ,可以很好地使用 cc 或者 gcc 將 C/C++ 項目協同構建為共享源代碼文件和其他組件,但是,當應用程序融入了任何其他人的改變時,構建所需要的時間可能是相當可觀的。即便您不是作為小組成員來編譯項目,重新編譯一個應用程序也會需要很長時間。ccac
借助
CVS,可以很好地使用 cc 或者 gcc 將 C/C++ 項目協同構建為共享源代碼文件和其他組件,但是,當應用程序融入了任何其他人的改變時,構建所需要的時間可能是相當可觀的。即便您不是作為小組成員來編譯項目,重新編譯一個應用程序也會需要很長時間。ccache 工具通過將頭文件高速緩存到源文件之中而改進了構建
性能,因而通過減少每一步編譯時添加頭文件所需要的時間而提高了構建速度。通過本文學習如何構建和安裝 ccache、如何在您現有的環境中使用它以及如何在小組
開發項目中改善編譯時間。您還將了解到如何同時使用 ccache 和 distcc 來使開發環境達到最佳性能。
在標准的編譯過程中,在 UNIX 下使用 C/C++ 開發應用程序通常需要用到一個編譯器(如 gcc)以及一個編譯工具,比如 make。make 和所有的 C 編譯器的問題在於 C 預處理程序(preprocessor)和頭文件的工作方式。觀察一個典型的 C 源文件,您會發現其中有很多由 #include 所引用的各種頭文件。
每一次編譯一個文件時,C 預處理程序(cpp)都會解析並引入每個頭文件以及這些頭文件引用到的任何文件。通過對內容進行解析,ccp 可以將一個相當基本的 1-KB 大小的源文件轉化為一個 8-KB 大小的源文件,在這個過程中,會合並入幾十個甚至幾百個頭文件。在典型的開發項目中,有很多與項目相關的頭文件可能會在不同的源文件中多次被引入,而且每個頭文件本身也可能引用很多其他頭文件。
在典型的編譯過程中,make 工具只編譯自上次編譯後發生修改的文件,這樣就在很大程度上簡化了編譯過程。例如,清單 1 中的目錄表明,foo.o 對象比相應的 foo.c 源文件的最後修改日期更新。同時,bar.o 比 bar.c 更新。使用一個經過適當配置的 Makefile,將只會從源文件編譯 foo.o。
make 將必須被編譯的文件限制在經過修改的那些源文件范圍之內,但是即使是使用 make,仍然有相當可觀的浪費。每一次編譯項目時,源文件在編譯為匯編語言和最終的機器代碼之前,都要通過 cpp 進行解析。對每一個文件來說,每一次可能都要重新解析頭文件。從編譯的全過程來看,您最後可能多次解析了相同的頭文件,浪費了處理器周期,更重要的是浪費了開發者的時間,因為他們要等待這一過程的完成。在一個團隊中,這一影響可能會更為明顯,因為多名開發者可能都會反復多次重復這一過程,在某一天甚至可能會同時進行。
清單 1. 一個示例源文件環境
total 808
-rw------- 1 mc mc 5123 24 Jul 14:17 bar.c
-rw------- 1 mc mc 39474 24 Jul 14:19 bar.o
-rw------- 1 mc mc 7856 24 Jul 14:17 foo.c
-rw------- 1 mc mc 28443 24 Jul 14:19 foo.o
-rwx--x--x 1 mc mc 319742 24 Jul 14:19 foobar*
-rw------- 1 mc mc 1045 24 Jul 14:21 foobar.h
使用 ccache
ccache(“compiler cache”的縮寫)工具會高速緩存編譯生成的信息,並在編譯的特定部分使用高速緩存的信息,比如頭文件,這樣就節省了通常使用 cpp 解析這些信息所需要的時間。如果您編譯清單 2 中的文件,假定 foobar.h 中包含對其他頭文件的引用,ccache 會用那個文件的 cpp-parsed 版本來取代 include 聲明。就那麼簡單。不是真正去讀取、理解並解釋其內容,ccache 只是將最終的文本拷貝到文件中,使得它可以立即被編譯。
清單 2. 源文件內容
#include "foobar.h"
void main(void)
{
}
安裝 安裝和使用 ccache 並不像您可能想像的那樣復雜。它不會取代或者以任何方式影響您原來的使用編譯器的方式,而是擔當了您與您的編譯器之間的一個接口,所以您可以根據需要選擇是否使用它。要安裝 ccache,需要從 Samba 小組或者一個本地鏡像(參閱本文最後的 參考資料)直接
下載源文件。解壓出文件的內容:
$ bunzip2 -c ccache-2.3.tar.bz2|tar xf -
切換到那個目錄:
$ cd ccache-2.3
配置: $ ./configure
編譯: $ make
最後,安裝 ccache:
$ make install
您就准備好開始使用了!
部署 如上所述,ccache 在您與您的普通編譯器之間進行工作。以 gcc 為第一個參數調用 ccache,而不是調用 gcc。例如,要在命令行中編譯一個文件,您通常會使用:
$ gcc foo.c
要使用 ccache,您應該輸入:
$ ccache gcc foo.c
像這樣對一個文件進行單獨的編譯,尤其是第一次使用 ccache 編譯那個文件時,您將不會看到有任何的幫助,因為編譯信息還沒有被高速緩存。所以,配置 ccache 永久地取代主要編譯器通常來說更為有效。設置 CC 環境變量的值來完成這一任務:
$ export set CC='ccache gcc'
如果您只是想為一個項目啟用 ccache,比如說編譯 Perl 等第三方工具時,那麼您或者可以使用環境變量,或者可以告知配置腳本或 make 命令使用哪個 C 編譯器。
控制高速緩存 默認情況下,ccache 使用當前用戶主目錄中的一個目錄($HOME/.ccache)來保持高速緩存信息。在團隊環境中,您應該使用一個集中的位置來進行高速緩存,這樣在編譯過程中每個人都可以使用高速緩存的信息。另一個環境變量 CCACHE_DIR 指定了高速緩存目錄的位置。在單機環境中,將這個環境變量設置為一個每一個需要它的人都可以訪問的目錄。使用通過 tmpfs 掛載的目錄可以獲得更高的速度,前提是您得有支持這一功能的存儲器。您的速度可能會再提高 10% 到 25%。
如果您是在
網絡中多台機器上使用 ccache,那麼要確保您共享的目錄要通過 NFS 導出(export)並掛載到每一個客戶機上。如果您希望獲得額外的加速,同樣可以使用 tmpfs 文件系統。
另外的一些選項讓您可以更深入地控制高速緩存設置:
CCACHE_LOGFILE 環境變量定義了使用高速緩存時生成的日志文件所處的位置。
在 ccache 中使用 -s 命令行選項來獲得關於高速緩存性能的統計數據(見清單 3)。
使用 -M 命令行選項來設置高速緩存的最大大小。默認是 1GB。高速緩存的設置會寫入到高速緩存目錄,所以您可以讓不同的用戶和組在不同的位置擁有不同大小的高速緩存。
-F 選項設置高速緩存目錄的最大文件數目,按 16 進制捨入。和 -M 相同,只是當您希望改變配置的時候才需要使用它。
-c 選項清空緩存。您通常不需要使用這個選項,因為 ccache 在執行過程中會更新信息,但是,如果您要重用一個沒有為某個文件所使用的高速緩存目錄,那麼就應該嘗試使用這個選項。
-C 選項完全清空高速緩存。
清單 3. ccache 高速緩存統計數據
cache hit 44
cache miss 152
called for link 107
compile failed 11
no input file 2
files in cache 304
cache size 8.8 MB
max cache size 976.6 MB
一旦設置了初始化選項並配置了期望的目錄和高速緩存大小,就不需要再做任何改動。沒有必要執行任何日常的維護。
組合 ccache 和 distcc 您可能已經想到了 distcc 這一來自 Samba 小組的另一個工具,它讓您可以將編譯過程分布到多台機器上。只需要為 make 添加多任務選項(使用 -j 命令行選項),它就可以有效地提高同步編譯的數目。distcc 系統的工作方式是,每台主機上有一個後台進程,接收最終預解析格式的源文件,然後在本地進行編譯,返回生成的對象文件。
如果使用得當,在每加入一個新的同樣節點時,編譯時間通常應該會以稍微低於線性的比率下降,不過您將只會在那些遠不只一個源文件的項目上看到這樣的影響,因為 distcc 只是分布全部源文件。
由於 distcc 所分布的是解析過的文件,所以您可以組合 ccache,它可以加速 C 預處理過程部分,同時 distcc 可以完成到對象代碼的實際編譯。要以這種方式來使用 distcc 和 ccache,需要在主機上配置 distcc,在主要的開發機器上配置 distcc 和 ccache。
現在在希望要編譯項目的機器上設置環境變量,如清單 4 所示。
清單 4. 使用 ccache 和 distcc 所需要的環境變量
export set DISTCC_HOSTS='localhost atuin nautilus pteppic kernel'
export set CCACHE_DIR=/Data/Cache/CCache
export set CCACHE_PREFIX=distcc
export set CCACHE_LOGFILE=/Data/Cache/CCache.log
export set CC='ccache gcc'
環境變量定義如下:
DISTCC_HOSTS 指定了將工作分布到哪些主機。
CCACHE_DIR 指定了高速緩存目錄的位置。
CCACHE_PREFIX 定義了當 ccache 調用真實的編譯器來編譯源文件(預處理之後)時所使用的前綴。
CC 設置首先使用的 C 編譯器的名稱(ccache)。
現在,當運行 make 時,如果使用了 -j 選項來指定要執行的同步編譯的數目,則首先使用 ccache 解析文件(如果需要,使用高速緩存),然後將其分布到某個 distcc 主機。
盡管 distcc 加速了編譯過程,但是它沒有改變環境的基本限制。例如,您不應該將 make 執行的同步作業的數目設置得大於可用 CPU 數目的兩倍。例如,如果您有四台兩路機器,那麼將作業值設置為超過 16 的值時將不再會觀察到有多大改善。
統計數據 既然一切都已就緒,現在可以觀察它帶來了多大的差別。在這裡,我已經運行了一系列編譯 Perl 的
測試。我們需要編譯一個足夠大的項目,因為 ccache 在高速緩存了解析過的頭文件時運轉效果最好。這正是在完成了標准配置(使用 configure.gnu)以後的 make 階段,它包括所有的步驟,甚至那些與編譯代碼無關的步驟。這些與編譯器無關的操作不會影響整體上的統計數據。
如前所述,在第一次編譯時,ccache 的影響不會為人所察覺。不同之處在於重新編譯時,在這個過程中您會重用先前的預處理程序。通過簡單地接觸(touch)主要 Perl 源文件目錄中的 C 源文件,得到了表 1 中的重新編譯時間。其中包括使用普通 gcc、ccache+gcc、ccache+distcc+gcc 所需的編譯時間,編譯在一個四節點網絡中進行並選用了不同的並發 distcc 作業數目值。
表 1. 重新編譯時間
環境 時間
gcc(第一次運行) 8m02.273s
gcc(重新編譯) 3m30.051s
ccache+gcc(第一次運行) 8m54.714s
ccache+gcc(重新編譯) 0m45.455s
ccache+distcc+gcc -j4 4m14.546s
ccache+distcc+gcc -j4(重新編譯) 0m38.985s
ccache+distcc+gcc -j8 3m13.020s
ccache+distcc+gcc -j8(重新編譯) 0m34.380s
只是使用了 ccache,編譯 Perl 就節省幾乎 3 分鐘(2 分 45 秒)的時間,所有的原因只是,ccache 已經保持了頭文件的預編譯版本,因而不需要再為每個源文件不斷重復運行 cpp。將 distcc 引入這一過程所得的結果是,整體上速度提高,重新編譯的時間快了一點點。