0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何使用in_place_t和相關(guān)標(biāo)簽高效創(chuàng)建

C語言與CPP編程 ? 來源:DeepNoMind ? 作者:DeepNoMind ? 2022-10-13 08:59 ? 次閱讀

本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風(fēng)格、安全性、可維護(hù)性、可移植性、多線程、性能、正確性等角度全面介紹了現(xiàn)代C++項(xiàng)目的最佳實(shí)踐。本文是該系列的第六篇。

C++最佳實(shí)踐:

1. 工具

2. 代碼風(fēng)格

3.安全性

4.可維護(hù)性

5.可移植性及多線程

6.性能(本文)

7.正確性和腳本

性能

盡量使用前置聲明

使用這種聲明方式:

//someheaderfile
classMyClass;

voiddoSomething(constMyClass&);

而不是這樣:

//someheaderfile
#include"MyClass.hpp"

voiddoSomething(constMyClass&);

同樣也使用于模板:

templateclassMyTemplatedType;

這種方式可以主動(dòng)減少編譯時(shí)間并重新構(gòu)建依賴關(guān)系。

注意: 前置聲明會(huì)阻礙內(nèi)聯(lián)和優(yōu)化,建議在發(fā)布版本中使用鏈接時(shí)優(yōu)化或鏈接時(shí)代碼生成。

避免不必要的模板實(shí)例化

模板不要隨便實(shí)例化,實(shí)例化過多模板,或者模板代碼多于必要的數(shù)量,會(huì)增加編譯代碼的大小和構(gòu)建時(shí)間。

更多示例請參考: Template Code Bloat Revisited: A Smaller make_shared[2]

避免遞歸模板實(shí)例化

遞歸模板實(shí)例化可能會(huì)給編譯器帶來很大的負(fù)擔(dān),并且代碼更加難以理解。

如果可能的話,考慮使用可變參數(shù)展開和折疊[3]。

分析構(gòu)建

可以使用Templight[4]工具分析項(xiàng)目的構(gòu)建時(shí)間,它需要花一些時(shí)間來構(gòu)建,但一旦這樣做了,可以用來替換clang++。

使用Templight進(jìn)行構(gòu)建之后,需要對(duì)結(jié)果進(jìn)行分析,templight-tools[5]項(xiàng)目提供了各種方法(建議使用callgrind轉(zhuǎn)換并使用kcachegrind對(duì)結(jié)果進(jìn)行可視化)。

隔離頻繁更改的頭文件

不要包含不需要的頭文件

編譯器必須處理看到的每個(gè)include指令,即使只是在看到#ifndefinclude保護(hù)符后立即停止,仍然必須打開文件并進(jìn)行處理。

include-what-you-use[6]是一個(gè)可以幫我們確定需要哪些頭文件的工具。

減少預(yù)處理器的工作

這是“隔離頻繁更改的頭文件”和“不要包含不需要的頭文件”的一般形式。類似BOOST_PP這樣的工具可能非常有用,但也給預(yù)處理器帶來了巨大的負(fù)擔(dān)。

考慮使用預(yù)編譯頭文件

使用預(yù)編譯頭文件可以大大減少大型項(xiàng)目的編譯時(shí)間,選定的頭文件被編譯成中間形式(PCH文件),編譯器可以更快處理。建議只將經(jīng)常使用但很少更改的頭文件定義為預(yù)編譯頭文件(例如系統(tǒng)頭文件和庫頭文件),以減少編譯時(shí)間。但必須記住,使用預(yù)編譯頭文件有幾個(gè)缺點(diǎn):

預(yù)編譯頭文件不可移植。

生成的PCH文件依賴于機(jī)器。

生成的PCH文件可能相當(dāng)大。

它會(huì)破壞頭文件依賴關(guān)系。由于有預(yù)編譯頭文件,每個(gè)文件都有可能包含標(biāo)記為預(yù)編譯頭文件的每個(gè)頭文件。因此,如果禁用預(yù)編譯頭文件,可能會(huì)導(dǎo)致構(gòu)建失敗。如果需要發(fā)布庫之類的項(xiàng)目,這可能是個(gè)問題。正因?yàn)槿绱?,?qiáng)烈建議在第一次構(gòu)建時(shí)啟用預(yù)編譯頭,而在后續(xù)構(gòu)建時(shí)將其關(guān)閉。

大多數(shù)常見的編譯器都支持預(yù)編譯頭文件,比如GCC[7]、Clang[8]和Visual Studio[9]。像cotire[10](cmake的插件)這樣的工具可以幫助我們在構(gòu)建系統(tǒng)中添加預(yù)編譯的頭文件。

考慮使用工具

工具并不意味著可以取代好的設(shè)計(jì)。

ccache[11],用于類unix操作系統(tǒng)的編譯結(jié)果緩存

clcache[12],cl.exe的編譯結(jié)果緩存(MSVC)

warp[13],F(xiàn)acebook的預(yù)處理器

將tmp放在Ramdisk上

詳見YouTube視頻: https://www.youtube.com/watch?v=t4M3yG1dWho

使用gold鏈接器

如果是在Linux上,考慮使用GCC的gold鏈接器(ld.gold)。

參考: gold: Google Releases New and Improved GCC Linker[14]

運(yùn)行時(shí)

分析代碼

在不分析代碼的情況下,無法真正找到瓶頸在哪里。

http://developer.amd.com/tools-and-sdks/opencl-zone/codexl/

http://www.codersnotes.com/sleepy

簡化代碼

代碼越清晰、越簡單、越容易閱讀,編譯器就越有可能更好的將其實(shí)現(xiàn)。

使用初始化列表

//This
std::vectormos{mo1,mo2};

//-or-
automos=std::vector{mo1,mo2};
//Don'tdothis
std::vectormos;
mos.push_back(mo1);
mos.push_back(mo2);

通過減少對(duì)象復(fù)制并調(diào)整容器大小,初始化列表能顯著提升性能。

減少臨時(shí)對(duì)象

//Insteadof
automo1=getSomeModelObject();
automo2=getAnotherModelObject();

doSomething(mo1,mo2);
//consider:
doSomething(getSomeModelObject(),getAnotherModelObject());

這類代碼將阻礙編譯器執(zhí)行move操作……

啟用移動(dòng)(move)操作

move操作是C++11中最受歡迎的特性之一,該操作允許編譯器通過移動(dòng)臨時(shí)對(duì)象從而避免額外的拷貝。

某些代碼(例如聲明自己的析構(gòu)函數(shù)或賦值操作符或拷貝構(gòu)造函數(shù))會(huì)阻止編譯器生成移動(dòng)構(gòu)造函數(shù)。

對(duì)于大多數(shù)代碼,下面這么一個(gè)簡單的定義:

ModelObject(ModelObject&&)=default;

...就足夠了,不過MSVC2013似乎不支持這段代碼。

避免shared_ptr拷貝

shared_ptr對(duì)象的拷貝成本比想象的要高得多,因?yàn)橐糜?jì)數(shù)必須是原子的和線程安全的。這條規(guī)則只是再次強(qiáng)調(diào)了上面的注意事項(xiàng): 避免臨時(shí)對(duì)象和過多的對(duì)象副本。僅僅因?yàn)槲覀兪褂昧?a href="http://www.ttokpm.com/tags/pi/" target="_blank">pImpl,并不意味著副本沒有代價(jià)。

盡可能減少拷貝和重分配

對(duì)于更簡單的情況,可以使用三元操作符:

//BadIdea
std::stringsomevalue;

if(caseA){
somevalue="ValueA";
}else{
somevalue="ValueB";
}
//BetterIdea
conststd::stringsomevalue=caseA?"ValueA":"ValueB";

使用立即調(diào)用的lambda[15]可以簡化更復(fù)雜的情況。

//BadIdea
std::stringsomevalue;

if(caseA){
somevalue="ValueA";
}elseif(caseB){
somevalue="ValueB";
}else{
somevalue="ValueC";
}
//BetterIdea
conststd::stringsomevalue=[&]("&"){
if(caseA){
return"ValueA";
}elseif(caseB){
return"ValueB";
}else{
return"ValueC";
}
}();

避免多余的異常

在正常處理期間,內(nèi)部拋出和捕獲的異常會(huì)降低應(yīng)用程序的執(zhí)行速度。由于調(diào)試器會(huì)監(jiān)視和報(bào)告每個(gè)異常事件,因此還會(huì)破壞調(diào)試器的用戶體驗(yàn)。最好盡可能避免內(nèi)部異常處理。

拋棄new

我們已經(jīng)知道不該使用裸內(nèi)存訪問,因此改用unique_ptr和shared_ptr,對(duì)吧?堆分配比棧分配昂貴得多,但有時(shí)不得不用。更糟的是,創(chuàng)建shared_ptr實(shí)際上需要在堆上分配2次。

然而,make_shared函數(shù)可以將其減少為一次。

std::shared_ptr(newModelObject_Impl());

//shouldbecome
std::make_shared();//(it'salsomorereadableandconcise)

優(yōu)先選擇unique_ptr而不是shared_ptr

可能的話,使用unique_ptr而不是shared_ptr。unique_ptr是不可復(fù)制的,因此不需要跟蹤副本,比shared_ptr性能更好。另外,類似于shared_ptr和make_shared的關(guān)系,應(yīng)該使用make_unique(C++14或更高版本)來創(chuàng)建unique_ptr:

std::make_unique();

目前的最佳實(shí)踐也建議從工廠函數(shù)返回unique_ptr,然后在必要時(shí)將unique_ptr轉(zhuǎn)換為shared_ptr。

std::unique_ptrfactory();

autoshared=std::shared_ptr(factory());

拋棄std::endl

std::endl表示刷新操作,等價(jià)于" " << std::flush。

限制變量作用域

變量應(yīng)該盡可能晚聲明,最好只在可以初始化對(duì)象時(shí)聲明。減小變量作用域可以減少內(nèi)存的使用,提高代碼效率,并幫助編譯器進(jìn)一步優(yōu)化代碼。

//GoodIdea
for(inti=0;i

對(duì)于C++17及以后版本,考慮在if和switch語句中初始化變量:

if(MyObjectobj(index);obj.good()){
//dosomethingifobjisgood
}else{
//dosomethingifobjisnotgood
}

Github上對(duì)此有專門的討論: https://github.com/lefticus/cppbestpractices/issues/52

優(yōu)先選擇double類型而不是float類型,但需要先測試

根據(jù)情況和編譯器的優(yōu)化能力,一種可能比另一種更快。選擇float意味著精度較低,并可能由于類型轉(zhuǎn)換而影響性能。在可向量化操作中,如果能夠犧牲精度,float可能更快。

double是C++中浮點(diǎn)值的默認(rèn)類型,因此推薦作為默認(rèn)選項(xiàng)。

參考下面的文章獲取更多信息: double or float, which is faster?[16]

優(yōu)先選擇++i而不是i++

...當(dāng)語義正確時(shí),前置自增比后置自增更快[17],因?yàn)榍爸米栽霾恍枰獎(jiǎng)?chuàng)建對(duì)象副本。

//BadIdea
for(inti=0;i

即使許多現(xiàn)代編譯器將這兩個(gè)循環(huán)優(yōu)化為相同的匯編代碼,選擇++i仍然是一種良好的實(shí)踐。你永遠(yuǎn)無法確定代碼會(huì)不會(huì)使用不帶優(yōu)化的編譯器,因此沒有任何理由不這樣做。此外,編譯器有可能只對(duì)整數(shù)類型進(jìn)行優(yōu)化,而不一定對(duì)所有迭代器或其他用戶自定義類型進(jìn)行優(yōu)化。

總而言之,如果前置自增操作符與后置自增操作符在語義上相同,那么使用前置自增操作符總是更好。

char是char, string是string

//BadIdea
std::cout<

看上去區(qū)別不大,但是" "必須被編譯器解析為const char *,必須在寫入流(或附加到字符串)時(shí)對(duì)?進(jìn)行范圍檢查,而' '是已知的單個(gè)字符,可以節(jié)約許多CPU指令。

如果多次調(diào)用效率低下的代碼,可能會(huì)對(duì)性能產(chǎn)生影響,更重要的是,考慮這兩種使用情況會(huì)讓我們更多的考慮編譯器和運(yùn)行時(shí)在執(zhí)行代碼時(shí)必須做什么。

永遠(yuǎn)不要用std::bind

std::bind的開銷(包括編譯時(shí)和運(yùn)行時(shí))幾乎總是比需要的更多,相反,我們只需使用lambda。

//BadIdea
autof=std::bind(&my_function,"hello",std::_1);
f("world");

//GoodIdea
autof=[](conststd::string&s){returnmy_function("hello",s);};
f("world");

了解標(biāo)準(zhǔn)庫

正確使用供應(yīng)商提供的標(biāo)準(zhǔn)庫中已經(jīng)高度優(yōu)化的組件。

in_place_t及相關(guān)內(nèi)容

知道如何使用in_place_t和相關(guān)標(biāo)簽高效創(chuàng)建諸如std::tuple、std::any和std::variant等對(duì)象。

審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2085

    瀏覽量

    73301
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4670

    瀏覽量

    67764
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1602

    瀏覽量

    48895

原文標(biāo)題:C++最佳實(shí)踐 | 6. 性能

文章出處:【微信號(hào):C語言與CPP編程,微信公眾號(hào):C語言與CPP編程】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    求時(shí)間t如何創(chuàng)建

    小白求助:已知一公式里有時(shí)間參數(shù)t。求時(shí)間t如何創(chuàng)建
    發(fā)表于 09-23 21:26

    Error: Can't place multiple pins assigned to pin location Pin_C1問題的解決

    在進(jìn)行nios實(shí)驗(yàn)中使用epcs flash ip核時(shí),在quartus II 進(jìn)行布局布線時(shí)出現(xiàn)以下錯(cuò)誤:Error: Can't place multiple pins assigned
    發(fā)表于 07-21 19:34

    BLE-Beacon Android應(yīng)用程序是用來創(chuàng)建標(biāo)簽的嗎?

    您好!我正在嘗試重新創(chuàng)建一個(gè)類似的應(yīng)用程序。有人知道Android組件是用來創(chuàng)建標(biāo)簽的嗎?當(dāng)做戴維 以上來自于百度翻譯 以下為原文Hi I am trying to recreate a
    發(fā)表于 12-04 11:44

    LabVIEW中創(chuàng)建XML文件,如何在標(biāo)簽名字中加入空格?

    我需要的標(biāo)簽名稱是這種格式,但是創(chuàng)建元素時(shí),標(biāo)簽名稱不能有空格、=和引號(hào),否則報(bào)錯(cuò).后來我嘗試用了"通過命名空間創(chuàng)建元素"控件,但生成的標(biāo)簽
    發(fā)表于 03-18 00:13

    如何將值寫入所有相同標(biāo)簽名稱的控件

    各位大師,我這里遇到個(gè)問題,在官方手冊里也沒找到相關(guān)說明.....就是當(dāng)控件標(biāo)簽一致時(shí),只能獲取到第一個(gè)創(chuàng)建標(biāo)簽名稱控件的值,包括設(shè)置值也是一樣;有沒有辦法可以將指定的值給到所有相同
    發(fā)表于 04-15 17:42

    歐盟新版ERP 能效標(biāo)簽EPREL產(chǎn)品注冊

    歐盟新版ERP能效標(biāo)簽EPREL產(chǎn)品注冊, 2021.5.1開始,supplier可以在歐盟網(wǎng)站的EPREL上,依照新ErP法規(guī)(EU) 2019/2015的要求去注冊產(chǎn)品,然后生成新版energy
    發(fā)表于 06-26 15:44

    怎樣使用NFC讀卡器讀取T2T標(biāo)簽

    我正在嘗試使用 NFC 讀卡器讀取 T2T 標(biāo)簽,Nucleo 擴(kuò)展板連接到 STM32F103 Nucleo 板。我正在使用 X-CubeExpansion_NFC3 中的示例 這個(gè)標(biāo)簽
    發(fā)表于 12-23 06:12

    OrCAD中創(chuàng)建總線及用法詳解

    OrCAD中創(chuàng)建總線及用法詳解,如何創(chuàng)建總線,菜單place->bus或者右側(cè)快捷按鈕
    發(fā)表于 12-02 10:18 ?2.6w次閱讀

    Synthesis_Place_&_Route

    Synthesis Place & Route
    發(fā)表于 02-19 16:48 ?0次下載

    一種基于標(biāo)簽概率相關(guān)性的微博推薦方法

    向微博用戶推薦對(duì)其有價(jià)值和感興趣的內(nèi)容,是改善用戶體驗(yàn)的重要途徑。通過分析微博的特點(diǎn)以及現(xiàn)有微博推薦算法的缺陷,利用標(biāo)簽信息表征用戶興趣,提出一種基于標(biāo)簽概率相關(guān)性的微博推薦方法LPCMR。首先,該
    發(fā)表于 11-17 14:54 ?13次下載
    一種基于<b class='flag-5'>標(biāo)簽</b>概率<b class='flag-5'>相關(guān)</b>性的微博推薦方法

    GC-PLACE操作手冊步驟免費(fèi)下載

    GC-PLACE軟件是一款專業(yè)的SMT編程工具,也是最常用的SMT編程軟件,小編為大家?guī)碓敿?xì)的使用教程,即使新手也不用擔(dān)心了,需要的朋友快快下載吧!
    發(fā)表于 07-01 08:00 ?0次下載
    GC-<b class='flag-5'>PLACE</b>操作手冊步驟免費(fèi)下載

    三菱PLC編程之標(biāo)簽的設(shè)置

    新建工程時(shí)選擇了“使用標(biāo)簽”的情況下,下述標(biāo)簽將被創(chuàng)建。
    發(fā)表于 06-25 18:50 ?8264次閱讀

    Chrome 瀏覽器已推出標(biāo)簽組自動(dòng)創(chuàng)建功能

    功能可以讓你對(duì)標(biāo)簽頁進(jìn)行打標(biāo)簽和分組,可折疊和展開標(biāo)簽組,消除標(biāo)簽欄的混亂。 不過,在 Chrome 87 中,谷歌還加入了新的標(biāo)簽組自動(dòng)
    的頭像 發(fā)表于 11-29 09:35 ?1323次閱讀

    快速了解R-Car Market Place網(wǎng)絡(luò)服務(wù)

    大家有沒有讀過我去年年底寫的博客“汽車業(yè)務(wù)的客戶價(jià)值(系列17):R-Car聯(lián)盟·合作伙伴計(jì)劃的介紹”?自這篇博客發(fā)布以來,瑞薩收到了大量反饋和在R-Car Market Place創(chuàng)建賬戶的申請。
    的頭像 發(fā)表于 12-18 10:57 ?1744次閱讀

    維克多中國榮獲Great Place To Work頒發(fā)的認(rèn)證證書

    近日,維克多中國榮獲由國際著名職場文化評(píng)估機(jī)構(gòu)Great Place To Work頒發(fā)的認(rèn)證證書,這一榮譽(yù)不僅是對(duì)我們長期以來營造的卓越職場文化的肯定,也標(biāo)志著我們在構(gòu)建員工滿意度、高效協(xié)作的工作環(huán)境方面取得了顯著成果。
    的頭像 發(fā)表于 03-26 13:32 ?392次閱讀