yadccC++ 分布式編譯系統(tǒng)
yadcc(Yet Another Distributed C++ Compiler)是一套騰訊廣告自研的分布式編譯系統(tǒng),用于支撐騰訊廣告的日常開發(fā)及流水線。相對于已有的同類解決方案,其針對實(shí)際的工業(yè)生產(chǎn)環(huán)境做了性能、可靠性、易用性等方面優(yōu)化。
yadcc 目前在騰訊 1700+ 核的集群中每天編譯 300,0000+ 個(gè)目標(biāo)文件,產(chǎn)出約 3~5TB,已經(jīng)持續(xù)穩(wěn)定運(yùn)營 8 個(gè)月。取決于代碼邏輯及本地機(jī)器配置,yadcc 可以利用幾百乃至 1000+ 核同時(shí)編譯(騰訊內(nèi)部使用 512 并發(fā)編譯),大大加快構(gòu)建速度。
具體簡介及技術(shù)細(xì)節(jié)可以參考技術(shù)文檔。
系統(tǒng)要求
- Linux 3.10 及以上內(nèi)核,暫不支持其他操作系統(tǒng);
- x86-64 處理器;
- 編譯
yadcc需要GCC 8 及以上版本的編譯器,基于yadcc進(jìn)行分布式編譯時(shí)可以支持其他更低版本編譯器。
基本原理
- 客戶端偽裝成編譯器(通常是通過
ln -sf yadcc g++創(chuàng)建的符號鏈接) - 通過將客戶端偽裝的編譯器加入
PATH頭部,這樣構(gòu)建系統(tǒng)就會實(shí)際執(zhí)行yadcc來編譯 -
yadcc會按照命令行對源代碼進(jìn)行預(yù)處理,得到一個(gè)自包含的的預(yù)處理結(jié)果 - 以預(yù)處理結(jié)果、編譯器簽名、命令行參數(shù)等為哈希,查詢緩存,如果命中,直接返回結(jié)果
- 如果不命中,就請求調(diào)度器獲取一個(gè)編譯節(jié)點(diǎn),分發(fā)過去做編譯
- 等待直到從編譯集群中得到編譯結(jié)果,并更新緩存
由于預(yù)處理時(shí)間通常遠(yuǎn)小于編譯時(shí)間,因此這樣可以降低單個(gè)文件的本地開銷。同時(shí),由于等待編譯結(jié)果時(shí)本地?zé)o需進(jìn)行操作,因此可以增大本地的編譯并發(fā)度(如8核機(jī)器通常可以make -j100),以此實(shí)現(xiàn)更高的吞吐。
需要注意的是,分布式編譯通常只能提高吞吐,但是不能降低單個(gè)文件的編譯耗時(shí)(假設(shè)不命中緩存)。因此,對于無法并發(fā)編譯的工程,除非命中緩存,否則分布式編譯通常不能加快編譯,反而可能有負(fù)面效果。
設(shè)計(jì)特點(diǎn)
系統(tǒng)由調(diào)度器、緩存服務(wù)器、守護(hù)進(jìn)程及客戶端組成:
- 對上層的構(gòu)建系統(tǒng)(Make、CMake,Blade、Bazel 等)透明,方便適配各種構(gòu)建系統(tǒng)。
- 調(diào)度器全局共享,所有請求均由調(diào)度節(jié)點(diǎn)統(tǒng)一分配。這樣,低負(fù)載時(shí)可允許客戶端盡可能提交更多的任務(wù),集群滿載時(shí)可阻塞新請求避免過載。
- 中心的調(diào)度節(jié)點(diǎn)也避免了需要客戶機(jī)感知編譯集群的列表的需要,降低運(yùn)維成本。
- 編譯機(jī)向調(diào)度器定期心跳,這樣我們不需要預(yù)先在調(diào)度器處配置編譯機(jī)列表,降低運(yùn)維成本。
- 分布式緩存避免不必要的重復(fù)編譯。同時(shí)本地守護(hù)進(jìn)程處會維護(hù)緩存的布隆過濾器,避免無意義的緩存查詢引發(fā)不必要的網(wǎng)絡(luò)延遲。
- 使用本地守護(hù)進(jìn)程和外界通信,這避免了每個(gè)客戶端均反復(fù)進(jìn)行TCP啟動等操作,降低開銷。另外這也允許我們在守護(hù)進(jìn)程處維護(hù)一定的狀態(tài),提供更多的優(yōu)化可能。
- 客戶端會和本地守護(hù)進(jìn)程通信,綜合控制本地任務(wù)并發(fā)度避免本地過載。
- 通過編譯器哈希區(qū)分版本,這允許我們的集群中存在多個(gè)不同版本的編譯器。
同時(shí),做了多層重試,確保不會因?yàn)榫W(wǎng)絡(luò)抖動、編譯機(jī)異常離線等工業(yè)場景常見的問題導(dǎo)致的不必要的失敗。
