這么多性能調(diào)優(yōu)工具,看看你知道幾個(gè)?
本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。
大家好,我是程序喵,雖然假期都快過去了,有很多朋友問程序喵怎么沒更文呢?是不是偷懶了!其實(shí)我這幾天沒閑著,一直在整理學(xué)習(xí)資料,昨晚終于完成了,估計(jì)這兩天會(huì)跟大家見面。
好了,我們CPP優(yōu)化系列正式開始!今天的文章里,我會(huì)介紹一些常用的工具,幫助大家找到代碼的“壞味道”(潛在的坑),進(jìn)而提升代碼質(zhì)量。
那到底什么樣的代碼才算是高質(zhì)量代碼呢?
對(duì)此我整理了一份腦圖:
如何能夠提升代碼質(zhì)量呢,除了我們自身過硬的編碼能力,還需要制定代碼檢查流程,一般代碼檢查有以下幾種方式:
代碼檢查要檢查的問題有:
腦圖中有一些代碼度量指標(biāo),它用于量化代碼質(zhì)量:
- 如果代碼的圈復(fù)雜度或認(rèn)知復(fù)雜度過大,可能函數(shù)本身實(shí)現(xiàn)的過于復(fù)雜,或可能因?yàn)榧軜?gòu)設(shè)計(jì)過于復(fù)雜,導(dǎo)致函數(shù)過于復(fù)雜。
- 如果函數(shù)嵌套過深,說明函數(shù)很可能出錯(cuò),需要仔細(xì)進(jìn)⾏⼈⼯評(píng)審,并且函數(shù)可能需要重構(gòu)。
- 如果模塊的扇入過大,說明模塊可能是公共模塊,需要⼈⼯評(píng)審接⼝是否是穩(wěn)定的,或模塊承擔(dān)過多職責(zé),可以考慮遵循單⼀職責(zé),分解模塊的職責(zé)。
- 如果模塊的扇出過大,說明該模塊依賴多個(gè)模塊,可以考慮把被依賴的多個(gè)模塊合并為⼀個(gè)模塊,重構(gòu)依賴的接⼝。
- 如果類的繼承樹過深,考慮在繼承樹的深度上是否有新的變化⽅向,考慮提出新的策略類,或其他設(shè)計(jì)模式來優(yōu)化繼承樹。
- 如果子類過多,檢查⼦類的實(shí)現(xiàn)中共同的地⽅,先考慮提出公共的中間⼦類,檢查是否可以通過橋接模式、裝飾模式、組合模式等結(jié)構(gòu)型模式重構(gòu)代碼。
上面腦圖所說的需要檢查的各種問題中,代碼和需求背離問題與代碼是否符合設(shè)計(jì)問題需要人工評(píng)審,成本較高,其它問題可以通過工具來檢測。
檢測工具主要分為靜態(tài)代碼分析工具和動(dòng)態(tài)代碼檢測工具。
靜態(tài)代碼分析工具主要用于靜態(tài)代碼分析,關(guān)于靜態(tài)代碼分析,它能夠根據(jù)規(guī)則幫助檢查代碼缺陷,然而,對(duì)于檢查規(guī)則能夠覆蓋的代碼,工具能夠工作的挺好,但對(duì)于規(guī)則沒有覆蓋的代碼,它卻無能為力,而且可能存在誤報(bào)問題。
靜態(tài)代碼分析是保證代碼質(zhì)量的重要手段,據(jù)說軟件開發(fā)中大概30%-70%的代碼邏輯設(shè)計(jì)和編碼缺陷都可以通過靜態(tài)代碼分析來發(fā)現(xiàn)和修復(fù)。它會(huì)掃描程序代碼,找出代碼中隱藏的錯(cuò)誤,如參數(shù)不匹配、有歧義的嵌套語句、錯(cuò)誤的遞歸、非法計(jì)算、空指針問題、越界問題、未初始化問題、內(nèi)存泄漏問題等。
靜態(tài)代碼分析工具的優(yōu)勢有:
- 自動(dòng)執(zhí)行靜態(tài)代碼分析,快速定位代碼隱藏錯(cuò)誤和缺陷
- 幫助代碼設(shè)計(jì)人員更專注于分析和解決代碼設(shè)計(jì)缺陷
- 減少在代碼人工檢查上花費(fèi)的時(shí)間,提高軟件可靠性并節(jié)省開發(fā)成本
舉例如下:
代碼規(guī)范檢查:由于拷貝粘貼造成兩個(gè)分支的代碼完全相同
- void func(int in, int &out) {
- if (in > 1) out++;
- else out++;
- out++;
- }
代碼缺陷檢查:沒有用的RAII
- void func() {
- std::lock_guard<std::mutex>(lk); // 臨時(shí)對(duì)象,語句結(jié)束后執(zhí)行析構(gòu),誤用的加鎖
- ...
- }
下面是一些常見的靜態(tài)代碼分析工具:
這里推薦一個(gè)常用的代碼質(zhì)量管理平臺(tái)SonarCube,SonarQube是一個(gè)管理代碼質(zhì)量的平臺(tái)(社區(qū)版免費(fèi)),用于管理代碼的質(zhì)量,它會(huì)從多個(gè)角度維護(hù)檢測代碼質(zhì)量,通過插件形式支持多種語言的代碼質(zhì)量管理和檢測。它可以安裝sonar-cxx插件,內(nèi)置了一系列C/C++代碼檢查工具,還可以應(yīng)用在CI/CD流程中,和Jenkins打通,可以在提交代碼后檢查代碼是否有壞味道,不符合規(guī)范的代碼就拒絕被合入master。
還有一個(gè)很好用的靜態(tài)代碼檢測工具是Facebook的infer,它最大的優(yōu)勢是可以靜態(tài)檢測代碼內(nèi)隱藏的內(nèi)存泄漏問題,而且免費(fèi)支持Android、C、OC語言。
靜態(tài)代碼分析工具可以在運(yùn)行前幫助我們檢測缺陷,只有30%-70%,但不是所有缺陷,很多缺陷需要在運(yùn)行時(shí)才會(huì)被發(fā)現(xiàn)。
其實(shí)我們還可以使用一些動(dòng)態(tài)分析工具,通過動(dòng)態(tài)分析工具可以準(zhǔn)確定位問題,而且誤報(bào)率低,但這與測試用例強(qiáng)綁定,查找缺陷的比例與測試用例的覆蓋率有關(guān),覆蓋率對(duì)于衡量代碼質(zhì)量有很大意義。
代碼覆蓋率的意義:
- 幫助我們找到未覆蓋部分的代碼,分析測試用例設(shè)計(jì)的是否充分,之后視情況決定是否可以補(bǔ)充測試用例。
- 檢測出代碼的壞味道,提示我們修改代碼,理清代碼邏輯關(guān)系,提升代碼質(zhì)量。
- 代碼覆蓋率高不能代表代碼質(zhì)量一定好,但代碼覆蓋率低,代碼質(zhì)量估計(jì)不會(huì)高到哪去,可以作為我們衡量代碼質(zhì)量的重要手段之一。
- 對(duì)于沒有覆蓋到的錯(cuò)誤,動(dòng)態(tài)分析工具也無能為力。在實(shí)際工作中,我們可以動(dòng)靜結(jié)合,多種檢查手段全都用上,可以更有效的提升代碼質(zhì)量。
動(dòng)態(tài)分析工具可以在程序運(yùn)行時(shí)發(fā)現(xiàn)代碼的缺陷,例如內(nèi)存問題、數(shù)據(jù)競爭、未定義行為等。
常用工具有GCC&Clang的Santizer系列:
- Asan-Address Sanitizer:緩存區(qū)溢出,內(nèi)存泄漏
- Tsan-Thread Sanitizer:并發(fā)問題
- Msan-Memory Sanitizer:未初始化內(nèi)存
- Ubsan-Undefined Behavior Sanitizer:未定義行為
- 編譯選項(xiàng)添加fsanitize=address/memory/thread/undefined
還有Valgrind工具:
- memchek:內(nèi)存問題,包括Asan和Msan
- helgrind:線程和并發(fā)問題
- cachegrind、callgrind、massif:幫助進(jìn)行性能優(yōu)化
使用各種工具與單元測試、功能測試、系統(tǒng)測試結(jié)合,提高覆蓋率,可以幫助我們發(fā)現(xiàn)更多缺陷。
前面的多數(shù)都是代碼分析工具,下面介紹一些性能分析工具,關(guān)于性能分析工具Brendan Gregg大佬的網(wǎng)站介紹的很詳細(xì),這里貼出來一張他總結(jié)的工具圖:
這張圖從Linux內(nèi)核的各個(gè)子系統(tǒng)出發(fā),匯總了對(duì)各個(gè)子系統(tǒng)進(jìn)行性能分析時(shí)可以選擇的工具。其實(shí)還有一些好用的工具,圖里沒有提到,這里重點(diǎn)介紹一下:
gprof:gprof是GNU工具之一,編譯的時(shí)候,它在每個(gè)函數(shù)的出入口加入了profiling的代碼,運(yùn)行時(shí)統(tǒng)計(jì)程序在用戶態(tài)的執(zhí)行信息,可以得到每個(gè)函數(shù)的調(diào)用次數(shù),執(zhí)行時(shí)間,調(diào)用關(guān)系等信息,簡單易懂。適合于查找用戶級(jí)程序的性能瓶頸,然而對(duì)于很多耗時(shí)在內(nèi)核態(tài)執(zhí)行的程序,gprof不適合。
Oprofile:Oprofile也是一個(gè)開源的profiling工具,它使用硬件調(diào)試寄存器來統(tǒng)計(jì)信息,進(jìn)行profiling的開銷比較小,而且可以對(duì)內(nèi)核進(jìn)行profiling。它統(tǒng)計(jì)的信息非常多,可以得到cache的缺失率,memory的訪存信息,分支預(yù)測錯(cuò)誤率等等,這些信息gprof得不到,但是對(duì)于函數(shù)調(diào)用次數(shù),它無能為力。
簡單來說,gprof簡單,適合于查找用戶級(jí)程序的瓶頸,而Oprofile稍微有點(diǎn)復(fù)雜,但是得到的信息更多,更適合調(diào)試系統(tǒng)軟件。
gperftools:Google出品,值得信賴,提供整個(gè)程序的熱點(diǎn)分布圖,找到性能瓶頸,然后可以針對(duì)性的進(jìn)行性能優(yōu)化,如圖:
我們平時(shí)編程過程中可能很多時(shí)候都會(huì)使用某些時(shí)間API來計(jì)算函數(shù)耗時(shí),使用方式可以看我的這篇文章:《詳細(xì)介紹下C/C++時(shí)間相關(guān)的那些函數(shù)》
那使用什么API效率更高呢,可以看下圖:
圖中的rdtsc使用較繁瑣而且不適用于所有平臺(tái)和編譯器,剩下的大家可以按需使用哈。
關(guān)于性能分析工具,程序喵整理了一份非常詳細(xì)的腦圖(精華全在腦圖里),以性能指標(biāo)分類,不同指標(biāo)使用什么工具進(jìn)行分析,都在圖里,目錄如下:
最后,公眾號(hào)后臺(tái)回復(fù)「性能分析工具」可以獲取完整高清PDF文件~
如果喜歡這篇文章,請(qǐng)點(diǎn)贊、在看,支持一下哦~謝謝!
參考資料
- https://stackoverflow.com/questions/375913/how-can-i-profile-c-code-running-on-linux
- https://zh.wikipedia.org/wiki/%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90
- https://www.cnblogs.com/bangerlee/archive/2012/08/30/2659435.html
- http://www.brendangregg.com/linuxperf.html
- https://www.cnblogs.com/youxin/p/7988479.html
- https://www.agner.org/optimize/
- https://zhuanlan.zhihu.com/p/141694060
- https://www.wyyuan.com/2018/11/06/%E8%AE%A4%E7%9F%A5%E5%A4%8D%E6%9D%82%E5%BA%A6/
- http://bitjoy.net/2017/02/07/introduction-to-performance-analysis-tools-in-linux/