ccache 是什麼?
ccache 藉由暫存編譯過的 object 檔, 可以減少不必要的重新編譯時間。
用法很簡單:
# 安裝
$ sudo aptitude install ccache
# 啟用 ccache
$ export PATH="/usr/lib/ccache:$PATH"
$ gcc ... # 這時會用到 /usr/lib/ccache/gcc
以我測試的例子來說, 從頭重新編譯一次是半小時左右, 裝了 ccache 後變成一分半。
依 ccache 官網所言, ccache 有可能重編譯不必要的程式, 但不會用到不對的暫存檔, 這也是使用這類工具時最重要的保證。
不過這篇的重點不在 ccache 的用法, 而是如何「知道如何安裝和使用它」。以前的文章或多或少有提到下文用的工具, 這篇比較有系統地用一個完整的例子使用它們。
已知
- ccache 替換 gcc 等編譯工具, 檢查編譯條件和檔案沒變時, 直接取得暫存檔作為編譯結果。
- 粗略地掃過 ccache manual, 得知是用 symbolic link 替換 gcc 為 ccache。
截至目前為止, 我們已得知 ccache 的核心運作原理, 接下來可以動手試看看。
安裝和使用過程
1. 首先確認是否能透過系統套件安裝 ccache:
$ aptitude search ccache
i ccache - Compiler cache for fast recompilation of C/C++ code
p ccache:i386 - Compiler cache for fast recompilation of C/C++ code
2. 再來看套件訊息, 確定沒搞錯套件, 還有看看版號是否夠新:
$ aptitude show ccache
Package: ccache
State: installed
Automatically installed: no
Version: 3.1.6-1
...
Description: Compiler cache for fast recompilation of C/C++ code
ccache is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again. Supported languages are C, C++,
Objective-C and Objective-C++.
Homepage: http://ccache.samba.org
對照官網版號 3.1.9, 3.1.6-1 還算 OK, 日後有必要再仔細看 release note 決定是否要抓原始檔重編。
3. 開始安裝:
$ sudo aptitude ccache
裝完後沒任何反應 ......, 依先前得知的訊息, 看看 gcc 有沒有被換掉:
$ which gcc
/usr/bin/gcc
$ ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 7 Nov 14 2012 /usr/bin/gcc -> gcc-4.6*
$ ls -l /usr/bin/gcc-4.6
-rwxr-xr-x 1 root root 353216 Apr 16 2012 /usr/bin/gcc-4.6*
結果 gcc 沒有被換掉 ......。
4. 腦袋卡住了一下下, 接著想到來確認 ccache 裝了什麼, 也許有些線索:
$ dpkg -L ccache
/.
/usr
/usr/share
...
/usr/share/man/man8/update-ccache-symlinks.8.gz
...
/usr/sbin/update-ccache-symlinks
/usr/bin
/usr/bin/ccache
/usr/lib
/usr/lib/ccache
裡面有個可疑的檔案 /usr/sbin/update-ccache-symlinks, 馬上來看看檔案內容。
結果是個 perl script, 從開頭得知重要的資訊:
6 my $ccache_dir = "/usr/lib/ccache";
7 my $old_gcc_dir = "/usr/lib/gcc";
8 my $new_gcc_dir = "/usr/lib/x86_64-linux-gnu/gcc";
9 my %old_symlinks; # Current compiler names in /usr/lib/ccache
10 my %new_symlinks; # Compiler names that should be in /usr/lib/ccache
11 my @standard_names = qw(cc c++);
對照後面的程式碼和這些變數名稱, /usr/lib/ccache 是關鍵的角色:
$ ls -l /usr/lib/ccache/
total 0
...
lrwxrwxrwx 1 root root 16 Aug 24 21:19 g++ -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 g++-4.6 -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 gcc -> ../../bin/ccache*
lrwxrwxrwx 1 root root 16 Aug 24 21:19 gcc-4.6 -> ../../bin/ccache*
...
Bingo!! 將 /usr/lib/ccache 加到 PATH 前面, 應該會有效果。但是, 要如何快速驗證 ccache 確實有發揮效果呢? 我可不想重編一堆檔案, 過個半小時才發覺「好像沒效」。
依前面的假設, ccache 編譯時會從暫存區取出編譯過的檔案, 所以第一次編譯時, ccache 應該也會寫入編譯過的檔案到暫存區。幸好開檔的 system call 只有一個 open(2), 這時就該 strace 上場了:
$ cd ~/tmp/
$ export PATH="/usr/lib/ccache:$PATH"
$ strace -eopen -f g++ t.cpp -c > log 2>&1
翻一下 log 會看到
10 [pid 18133] open("/home/fcamel/.ccache/tmp/t.tmp.fc-vm.18132.ii", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0666) = 4
11 [pid 18133] open("/home/fcamel/.ccache/tmp/tmp.cpp_stderr.fc-vm.18132", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0666) = 4
由此順便找到暫存目錄在 $HOME/.ccache 下。日後有需要也可以到這裡清清暫存檔。
有興趣的話可以用 strace 追更多 system call, 比對 open 開啟的 file descriptor (fd) 和後續 read、write 操作的 fd, 可以證實更多推測。到此就大功告成了!
小結
相信以 ccache 這樣成熟的套件, 仔細翻翻官網文件、man page 或 google 一下, 就可以找到答案。不過了解如何使用這些系統工具以後, 日後遇到文件不詳細或不易 google 的套件時, 有機會自行找出一線生機。