[分享] 使用 ccache 加速編譯

看板C_and_CPP (C/C++)作者 (pomelocandy)時間3年前 (2021/03/25 15:15), 編輯推噓5(504)
留言9則, 6人參與, 3年前最新討論串1/1
網誌好讀版: https://yodalee.me/2021/03/2021_ccache/ 發在這裡好像不太適合,但最需要編譯的應該也是這裡: ---- 故事是這樣子的,小弟在公司工作內容,要維護公司產品核心的engine 要維護當然會需要把程式碼從版本控制裡簽出來,修改、編譯後測試修正有沒有問題 而編譯一直以來都非常花時間。 本文介紹的 ccache 是 compiler cache 的簡稱 https://ccache.dev/ 會在編譯時存下檔案內容的 hash 與編譯結果 在未來如果有相同的檔案要編譯的時候,就不用再次呼叫 gcc/g++ 進行耗時的編譯 只要把存下的 object 檔從 cache 裡面抓出來就行了。 ---- 在編譯加速上,小弟的公司已經做了許多努力,包括: * prebuilt library:因為公司有很多共用的 library,每天某個時間點 電腦會自動簽出當天的程式碼並編譯所有 library 如果你沒改到 library,不簽出這個 library 就不用重編譯 連結定期建好的 library 即可。 * 公司也自幹了一套編譯工具,可以幫你把 code 灑到許多(一般預設是80台) 機器上平行編譯, 有點類似 distcc,可以大幅減少編譯時間。 話雖如此,分散式編譯從零開始編譯到產生執行檔仍然需要 11 分 33 秒 如果不用分散式編譯用單機編譯則需要 23 分 36 秒 目前程式碼裡,有一些 cpp 檔不知道是不是寫太長或太複雜 光編譯單一檔案都要編超久,即使有分散式編譯也會卡住整個編譯流程。 最糟的是,隨著簽出的 library 數量愈多,編譯時間也會愈長 某個極度複雜用程式寫程式的元件,單機編譯可能要花上兩個小時 後來忘了我在哪個場合,聽到強者我同學在 Google 大殺四方的小新大大 提到 ccache,就決定來試試看能加速多少? ## 安裝 不是管理員無權動工作站的內容,我在工作站上只有找到一款 ccache-swig,版本是 1.2.4,這版本實在有點太老了 因此我決定自己去 github 載最新的 code 回來自己編譯 https://github.com/ccache/ccache 建議至少要用到 ccache 3,才有支援下面會提到的 hash_dir 的功能。 ccache 採用的是 Cmake,載下來之後照安裝步驟進行編譯即可: mkdir build cd build cmake -DZSTD_FROM_INTERNET=ON .. make make install ccache 背後採用 facebook 的 libzstd 來壓縮、儲存快取的 .o 目的檔 同樣因為我的機器沒裝 libzstd,所以讓 cmake 去網路上抓。 ## 設定 裝完之後 ccache 會需要一些設定,這裡只列出我有設定的,其他的就請參閱文件。 https://ccache.dev/manual/4.2.html 每個 ccache 的設定都有兩種設定方式,一種是用環境變數 一種是寫到 ccache.conf檔案中 ### ccache directory ccache 快取儲存的位置,我用 CCACHE_DIR 環境變數來設定 一般預設會使用 $HOME/.cache 作為儲存位置 但我們工作站有限制家目錄的容量,因此我用 softlink 從 $HOME/.cache 連到大容量磁碟另一個 .cache 資料夾。 我不清楚 cache directory 放在硬碟、固態硬碟、Ramdisk 會不會對效能有影響 以速度、頻繁讀寫又可以限制容量的性質來看,ccache 放在 Ramdisk 上應該滿合理的 可惜在工作站上權限不夠沒辦法自建掛載 ramdisk QQ。 ccache directory 下的 ccache.conf 則是我們的設定檔。 ### 最大容量 執行 ccache -M 2G 或在設定檔中留下 max_size = 2G 來設定 ccache 可使用的最大容量 因為使用了 zstd 的關係,ccache 用的 cache 空間不會很大 我編譯了應該有幾百 MB 的執行檔,debug/release 各一套近 1000 個目的檔 也只用掉 250 MB 的 cache 空間,2G 對一般人來說應該很夠用了。 ### hash_dir hash_dir = false 在寫程式難免需要 debug build,而 debug build 會在目的檔內留下編譯時 資料夾的絕對路徑,這會讓 ccache 失效 因為在不同資料夾內的同一個檔案,會被 ccache 視為不同檔案而重編譯 而工作上為了修正不同的問題,在不同地方同時簽出 code 司空見慣。 設定 hash_dir = false 可以讓 ccache 忽視掉檔案的絕對路徑資訊 即使不同資料夾內的編譯也能共享 cache, 這也是為什麼上面說一定要用 >3 的版本 因為沒有 hash_dir ccache 的效率會大幅下降。 對這個問題有其他的解決方式,請參考Compiling in different directories https://ccache.dev/manual/4.2.html#_compiling_in_different_directories ## 測試 ccache 的使用方式很簡單,把 gcc/g++ 呼叫改成 ccache gcc/g++ 如果用的是 Makefile,最簡單的就是設定 CC, CXX 兩個變數 讓 Makefile 替換掉編譯的指令。 小弟因為公司自幹一套編譯系統,花了一點時間跟整合 team 問了該如何 設定自己的編譯器 而且因為上述的分散式編譯,會把工作丟到其他機器去執行 而遠端機器的函式庫舊到無法執行我用最新函式庫編譯的 ccache 導致我下面的測試都是把分散式編譯關掉改用單機編譯 測試結果可能會比用分散式編譯還要好。 ### 測試步驟 以下測試流程: 1. 用 ccache -Cz 清空快取與 ccache 統計資料 2. 簽出 baseline 進行 debug build 3. 簽出 enhance 進行 debug build 4. 在 baseline 進行 opt build 5. 在 enhance 進行 opt build 6. ccache -s 觀看統計資料 我們的程式在 build 的時候,除了編譯各個檔案外 還有一些時間花在從程式裡剖析 prototype 以及最後連結執行檔的時間 這些都不是 ccache 可以加速的步驟;4. 5 步時,因為 2.3 步 debug build 已經做過從程式裡剖析 prototype這件事,編譯 c/cpp 檔案的時間佔比會更高。 ### 測試結果 Baseline Enhance Gain Debug Build 29 min 39 sec 11 min 51 sec 2.3x Opt Build 23 min 36 sec 4 min 47 sec 4.9x 看這個結果我們應該可以推估從程式裡剖析 prototype 這件事大概耗時六分鐘 (而且這步沒有平行化處理)。 這個結果比我想得還要厲害一點,編譯佔比高的 Opt build 幾乎快了五倍。 當然,這裡並沒有測試分散式編譯下 ccache 的影響 因為分散式編譯下編譯佔的時間會少很多,想必效能增長不會這麼大。 統計結果當個參考就好 ccache -s cache directory /home/ipban/.ccache primary config /home/ipban/ccache.conf secondary config (readonly) /home/ipban/myinstall/etc/ccache.conf stats updated Wed Mar 24 12:46:34 2021 stats zeroed Wed Mar 24 11:48:15 2021 cache hit (direct) 459 cache hit (preprocessed) 2 cache miss 465 cache hit rate 49.78 % called for link 4 cleanups performed 0 files in cache 928 cache size 246.8 MB max cache size 2.0 GB ## 結語 導入 ccache 對編譯速度改進比預想還要好 目前小弟應該會跟公司的整合 team 連絡看看 看能不能將 ccache 導入我們的編譯流程中。 畢竟如果在每天定期的編譯時,也能把 ccache 的結果一併建出來 放在共用的磁碟內,大家把 cache dir 設到那裡去,很可能可以省下巨量的編譯時間 應該是滿值得的。 -- ______ |\ / \ | \ / ● ● \ |__\ / ______ \ | /   \__/   \___| /______________\ | -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 149.117.214.29 (美國) ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1616656531.A.B09.html

03/25 15:41, 3年前 , 1F
為 CompilerDev 板感到難過 QQ
03/25 15:41, 1F

03/25 16:57, 3年前 , 2F
那是編譯器開發不是如何使用編譯器吧XD
03/25 16:57, 2F

03/26 19:36, 3年前 , 3F
前幾天在build clang的時候也剛好看到這個,一直還沒測測
03/26 19:36, 3F

03/26 19:36, 3年前 , 4F
看就看到這篇了,感謝實測XD
03/26 19:36, 4F

03/27 01:36, 3年前 , 5F
推 原來有編譯器板喔==
03/27 01:36, 5F

03/28 20:48, 3年前 , 6F
推 實用
03/28 20:48, 6F

04/05 19:53, 3年前 , 7F
ramdisk 可用 /run/shm (tmpfs)
04/05 19:53, 7F

04/05 19:55, 3年前 , 8F
或是 /dev/shm ,這和 /tmp 一樣都是公用的,不太會有權限
04/05 19:55, 8F

04/05 19:55, 3年前 , 9F
問題。
04/05 19:55, 9F
文章代碼(AID): #1WN3YJi9 (C_and_CPP)
文章代碼(AID): #1WN3YJi9 (C_and_CPP)