分析C++工程編譯時(shí)間過長的原因

      網(wǎng)友投稿 2133 2022-05-29

      【引言】

      大家好,最近接到一個(gè)任務(wù),是分析一下為何一些C++工程編譯時(shí)間過長,是否有好的方案來改善一下編譯的速度。本文我們來探討一下可能影響到C++編譯時(shí)間的一些因素。

      【頭文件相關(guān)問題】

      【沒有把標(biāo)準(zhǔn)庫頭文件放在預(yù)編譯頭文件中】

      預(yù)編譯頭文件中應(yīng)該放入很少改動(dòng)的那些頭文件,比如系統(tǒng)級(jí)的頭文件,標(biāo)準(zhǔn)庫的頭文件,從而節(jié)省反復(fù)編譯造成的時(shí)間開銷。

      不要把已有頭文件放入預(yù)編譯頭文件中,由于私有頭文件會(huì)經(jīng)常改動(dòng),每次改動(dòng)都會(huì)導(dǎo)致整個(gè)預(yù)編譯頭文件編譯一次,會(huì)消耗很多時(shí)間。

      【沒有把標(biāo)準(zhǔn)庫頭文件放在預(yù)編譯頭文件中的問題修復(fù)】

      把不常改動(dòng)的標(biāo)準(zhǔn)庫頭文件和系統(tǒng)級(jí)頭文件放入預(yù)編譯頭文件中。

      【頭文件在一個(gè)編譯單元中的多次引用編譯】

      如果沒有預(yù)編譯頭判斷檢查守衛(wèi)的話,一個(gè)頭文件被引入幾次就會(huì)被編譯幾次,會(huì)消耗不必要的時(shí)間。

      【頭文件在一個(gè)編譯單元中的編譯問題的修復(fù)】

      在定義頭文件時(shí)使用如下格式:

      #pragma?once

      #ifndef?filename_h

      #define?filename_h

      //?Header?declarations?/?definitions

      #endif

      如上的示例是預(yù)編譯頭的條件判斷,用這樣的判斷可以避免一個(gè)頭文件在一個(gè)編譯單元中被編譯多次。

      【引入了不需要的頭文件】

      有時(shí)候在寫程序的時(shí)候,由于種種原因工程師引入了一些不需要的頭文件。這些引入的頭文件會(huì)消耗編譯的時(shí)間。

      【引入了不需要的頭文件問題的修復(fù)】

      刪除此種情況下引入的不必要頭文件。

      【頭文件的重復(fù)多次編譯問題】

      一個(gè)工程中一般存在很多個(gè)編譯單元,一個(gè)編譯單元對(duì)應(yīng)一個(gè)編譯輸出如obj, so文件,每個(gè)編譯單元會(huì)包含成百上千個(gè)頭文件,這些頭文件在編譯對(duì)應(yīng)編譯單元時(shí)會(huì)被加載并編譯。

      一個(gè)很普遍的情況是一個(gè)工程中的頭文件會(huì)被多個(gè)編譯單元引用,這樣在編譯的時(shí)候,這些頭文件會(huì)被編譯多次。這些時(shí)間開銷累加起來是非常可觀的。

      【頭文件的重復(fù)多次編譯問題修復(fù)】

      要修復(fù)頭文件重復(fù)多次編譯的問題,我們需要對(duì)編譯單元進(jìn)行整合,盡可能地減少多個(gè)編譯單元對(duì)于某個(gè)頭文件的依賴情況。

      【頭文件中使用using namespace的編譯問題】

      這種情況可能會(huì)引起名字沖突的編譯錯(cuò)誤。所以要避免這種情況。

      【頭文件中使用using namespace的編譯問題的修復(fù)】

      可以這樣修復(fù):

      分析C++工程編譯時(shí)間過長的原因

      class?MyClass

      {

      private:

      NameSpace1::Class1?_member;

      }

      或者

      class?MyClass

      {

      namespace?n=NameSpace1;

      private:

      n::Class1?_member;

      }

      【間接引用頭文件的編譯問題】

      下面的例子是一個(gè)間接引用頭文件的例子。在ClassB的頭文件中引入ClassA的頭文件以后,編譯器會(huì)打開ClassA的頭文件進(jìn)行編譯。這個(gè)消耗實(shí)際上我們是可以避免的。

      #include?"classa.h"

      class?ClassB{

      ClassA?*m_pClassA;

      };

      在上面的代碼中,我們引入了classa的頭文件;

      【間接引用頭文件問題修復(fù)】

      class?ClassA;

      class?ClassB{

      ClassA?*m_pClassA;

      };

      在上面的代碼中,我們通過前向聲明ClassA的方式避免引入classa的頭文件。

      通過以上的改動(dòng)可以提高編譯的速度。

      具體到數(shù)量級(jí)要根據(jù)具體項(xiàng)目來定,這里有個(gè)粗略的估算,如果你的頭文件比較龐大,而又在一個(gè)項(xiàng)目中被多個(gè)工程所引用,通過這個(gè)方法有可能幫助你減少幾個(gè)小時(shí)的編譯時(shí)間。

      【一個(gè)頭文件種包含了多個(gè)不相關(guān)的功能組定義】

      如果在一個(gè)頭文件中包含了多個(gè)不相關(guān)的功能組,這個(gè)頭文件必然會(huì)被不同的模塊功能組引用,這可能會(huì)導(dǎo)致這個(gè)頭文件被多次編譯。從而導(dǎo)致了編譯時(shí)間的增長。

      【一個(gè)頭文件種包含了多個(gè)不相關(guān)的功能組定義問題修復(fù)】

      修復(fù)這個(gè)問題的方法是把多個(gè)功能組從這一個(gè)頭文件中分離出來,創(chuàng)建對(duì)應(yīng)的功能單一的頭文件。然后在對(duì)應(yīng)的功能組中只引用對(duì)應(yīng)的那個(gè)頭文件。

      【其他不好的頭文件使用習(xí)慣】

      【頭文件中引入cpp文件】

      在公共頭文件中引入源文件不是一個(gè)好習(xí)慣。有時(shí)候程序員希望在多個(gè)源文件中使用一些共享代碼,于是通過引入一個(gè)源文件的方式來做。

      如果屬于類似情況的話,可以把這些共享代碼放在內(nèi)部的頭文件中。

      【在公共頭文件中放置了過多的信息導(dǎo)致信息泄露】

      在開發(fā)共享庫的時(shí)候,我們會(huì)把暴露出的頭文件如何調(diào)用的信息說清楚就行了,有時(shí)候畫蛇添足的說明這個(gè)功能是如何開發(fā)實(shí)現(xiàn)的。

      刪除與公共頭文件調(diào)用無關(guān)的信息。

      【頭文件不能自編譯】

      把頭文件引入一個(gè)空的源文件進(jìn)行編譯測(cè)試,如果存在編譯錯(cuò)誤,說明該頭文件無法自編譯。

      頭文件無法自編譯會(huì)在工程代碼龐大起來以后發(fā)生一些隨機(jī)的誤導(dǎo)性的編譯錯(cuò)誤,使得問題排查工作變得艱難。

      需要通過自下而上的方法修復(fù)頭文件不能自編譯的問題。

      【模板相關(guān)的問題】

      【模板的使用會(huì)增加編譯時(shí)間】

      【單層模板】

      在C++中使用模板時(shí),編譯器會(huì)把每處使用模板的地方展開再編譯,這個(gè)展開的過程自然會(huì)增加時(shí)間的開銷。

      【嵌套模板】

      template

      int?add(T?t,?ArgTypes...?args)

      {

      return?t?+?add(args...);

      }

      更為可怕的情況是當(dāng)在代碼中使用嵌套模板的時(shí)候,也就是模板中再使用另一層模板或者更多層模板,編譯器需要首先解釋并展開這些模板以及嵌套的模板,這個(gè)時(shí)候編譯器解釋和展開的時(shí)間消耗會(huì)是驚人的。

      【多個(gè)模板參數(shù)】

      template?

      struct?foo?{?};

      在上例中,如果T有8種可能,U有8種可能,編譯器會(huì)展開成64種實(shí)現(xiàn)。這會(huì)極大的增加編譯時(shí)間的開銷。

      【模板會(huì)導(dǎo)致超復(fù)雜的變量類型和超長的變量函數(shù)名】

      由于編譯器在解釋展開模板時(shí)是通過程序來做的,編譯程序可能會(huì)生成極其復(fù)雜的變量類型以及超級(jí)長度的變量名和函數(shù)名。這些程序生成的代碼會(huì)額外的增加編譯時(shí)間和鏈接時(shí)間。

      【模板編譯問題的修復(fù)】

      盡量不使用嵌套模板。

      盡量使用一個(gè)模板參數(shù)。

      【過長的命名】

      com.sun.java.swing.plaf.nimbus.InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

      上例是Java類的一個(gè)命名例子。

      有時(shí)候程序員會(huì)選擇使用過長的命名,比如類名,函數(shù)名,變量名,文件名等等,這些也會(huì)導(dǎo)致編譯時(shí)間和鏈接時(shí)間的增長。

      【過長命名問題的修復(fù)】

      盡量使用短而又可理解的命名。

      【過多的編譯單元】

      過多的編譯單元無疑會(huì)增加編譯和鏈接的時(shí)間。

      【過多的編譯單元問題修復(fù)】

      可以通過腳本在編譯前把源文件合并。比如sqlite的編譯過程,?這樣可以節(jié)省編譯和鏈接的時(shí)間。

      https://sqlite.org/src/doc/trunk/README.md

      For example:

      tar xzf sqlite.tar.gz??? ;#? Unpack the source tree into "sqlite"

      mkdir bld??????????????? ;#? Build will occur in a sibling directory

      cd bld?????????????????? ;#? Change to the build directory

      ../sqlite/configure????? ;#? Run the configure script

      make???????????????????? ;#? Run the makefile.

      make sqlite3.c?????????? ;#? Build the "amalgamation" source file

      make test??????????????? ;#? Run some tests (requires Tcl)

      以上展示的是sqlite的編譯過程。其中高亮部分就是編譯一個(gè)合并后的源文件。

      【使用并行編譯機(jī)制】

      很多編譯器和開發(fā)工具支持多內(nèi)核多CPU條件下的并行編譯。比如gnu make下使用-j[N]選項(xiàng): make –j 4,?同時(shí)啟動(dòng)4個(gè)任務(wù)。

      Visual Studio下使用并行模式編譯。

      在可以充分使用多核多CPU的情況下,并行編譯可以提高編譯速度。

      【編譯優(yōu)化機(jī)制最小化】

      總的規(guī)律是使用的優(yōu)化選項(xiàng)越多,編譯器做的事情就越多,花的時(shí)間就越長。

      【共有代碼的共享最大化】

      把不經(jīng)常更改的代碼放在庫里面,通過預(yù)編譯設(shè)置,可以提高編譯速度,同時(shí)把大量的共有代碼放在一個(gè)so或者dll?里面又可以提高鏈接速度。

      【提高電腦配置】

      通過使用更高的CPU,更多的內(nèi)存,以及固化硬盤等措施提高編譯電腦的性能來提高編譯速度。

      【提高網(wǎng)絡(luò)速度】

      如果在編譯過程中需要訪問網(wǎng)絡(luò),那么通過提高網(wǎng)速的方法也可以提高編譯的速度。

      【小結(jié)】

      以上內(nèi)容對(duì)于C++工程編譯過程中可能存在的問題,尤其是影響編譯速度的問題進(jìn)行了探索。?如果您所在的團(tuán)隊(duì)正在進(jìn)行相關(guān)的優(yōu)化,不妨做個(gè)參考,希望本文能有所幫助。歡迎大家評(píng)論,留言,批評(píng)和指正。

      C++

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:個(gè)推CTO談數(shù)據(jù)中臺(tái)(下):從演進(jìn)、經(jīng)驗(yàn)到規(guī)劃
      下一篇:android如何提升性能技巧(上)
      相關(guān)文章
      亚洲福利视频一区| 亚洲专区先锋影音| 久久久久久久久亚洲| 国产成人亚洲综合无码| 朝桐光亚洲专区在线中文字幕 | 亚洲精品乱码久久久久久按摩 | 亚洲午夜在线一区| 亚洲性一级理论片在线观看| 亚洲日韩乱码久久久久久| 亚洲高清免费在线观看| 亚洲综合日韩中文字幕v在线 | 亚洲色WWW成人永久网址| 亚洲色婷婷综合久久| 亚洲无线码在线一区观看| 亚洲人成伊人成综合网久久久| 亚洲日本va中文字幕久久| 亚洲va中文字幕无码久久不卡| 亚洲va久久久噜噜噜久久男同| 久久精品国产精品亚洲色婷婷| 久久久久亚洲AV成人无码网站| 久久久亚洲欧洲日产国码aⅴ| 亚洲一区综合在线播放| 亚洲视频在线观看免费视频| 亚洲成AV人片久久| 最新亚洲春色Av无码专区| 亚洲AV无码成人网站在线观看| 国产精品亚洲精品日韩动图| 亚洲精品无码久久久久AV麻豆| 精品国产人成亚洲区| 国产亚洲综合一区柠檬导航| 亚洲国产美国国产综合一区二区| 亚洲黄网站wwwwww| 日韩亚洲国产高清免费视频| 色欲aⅴ亚洲情无码AV蜜桃| 无码欧精品亚洲日韩一区夜夜嗨| 亚洲一区二区视频在线观看| 亚洲精品无码久久久久去q| 亚洲AV永久无码区成人网站| 2022年亚洲午夜一区二区福利| 亚洲人成片在线观看| 亚洲欧美国产国产综合一区|