C++性能優化常用技巧

一. 選擇合適的數據結構

1.1 map與unordered_map的選擇

如果僅僅只需要使用到快速查找的特性,那么unordered_map更加合適,他的復雜度是O(1)。如果還需要排序以及范圍查找的能力,那么就選擇map。

1.2 vector與list的選擇

通常情況下,順尋存儲一些數據,然后通過下標進行查找,就選擇vector。那么什么時候選擇list呢?常見的一個用法就是在LRU 緩存的實現中,我們會使用list來存放緩存結點,而不是vector,主要原因就在于list的插入和刪除是高效的。

1.3 unordered_map與自定義查找表的選擇

如果不需要復雜的哈希函數,僅通過數組下標的形式就能完成哈希,比如key是26個字母或者連續的數字。此時只需要定義一個數組就能實現哈希存儲(自定義查找表)。

二. 選擇合適的算法

算法對性能的影響是值得考慮的,以排序算法舉例:

  • 快速排序:適合數據比較均勻的場景,如果數據已經有序或者接近有序,此時快速排序會退化到O(n*n)的復雜度。
  • 堆排序:適合求topK問題。
  • 歸并排序:O(n)的空間復雜度,但是排序性能不會因為數據本身已經有序而退化。
  • 冒泡排序:一般情況下不推薦此排序。性能較差。

三. 避免不必要的拷貝

3.1 使用引用

引用可以直接與被引用對象共享同一份存儲,可以認為引用是給對象起一個別名。如下示例代碼:

// 形參使用引用
void show(const std::string& str)
{// do something
}std::vector<std::string> strs;
/*
對strs進行填充
*/
// 使用引用接收返回值,前提是函數返回對象的引用
const std::string& str = strs[1];
3.2 使用移動語義

移動語義一般用在初始化對象或者給對象賦值時,可以避免對象的拷貝。如下代碼:

std::string stra = "abc";
// 拷貝stra對象
std::string strb = stra;
// 移動strb的資源到strc中
std::string strc = std::move(strb);
3.3 返回值優化(RVO)

通常編譯器都有RVO優化的功能,它允許直接在調用者的棧幀上構造對象,從而避免了額外的拷貝和資源析構的開銷。如下代碼:

class A {
public:A() {}~A() {}A(const A& rhs) {}A(A&& rhs) {}
};int func()
{A a;return a;
}int main()
{A a = func();return 0;
}

上述代碼中,返回值優化之后,類A的構造函數和析構函數只被調用一次,不會產生臨時變量的構造與析構,以及拷貝構造的過程。

四. 合適的內存管理

4.1 智能指針

在管理動態內存時,智能指針可以幫助我們實現更可靠的內存管理,避免內存泄漏等嚴重問題。C++11中提供了unique_ptr和shared_ptr兩個智能指針,那么該如何選擇呢?

  • shared_ptr:共享智能指針。一個對象可能在多個地方被共享。
  • unique_ptr:獨占智能指針。一個對象只能被一個智能指針對象所擁有。

通常情況下,如果能明確是獨占的場景,那么就選擇unique_ptr,雖然shared_ptr也能保證正確性,但是后者性能要比前者差30%。因為uniqe_ptr更接近裸指針,而shared_ptr內部實現相對復雜(引用計數、控制塊等)。

4.2 內存池

內存池是一個預先申請和分配好的內存區域。在需要申請內存資源時,可以直接在內存池中獲取,在釋放內存時,將內存返還給內存池。從而避免了頻繁的內存分配和釋放的過程。google的tcmalloc就提供了這樣的功能。

4.3 對象池

對象池同樣也是一種預先申請和分配內存的技術,區別于內存池的是,其針對的是特定的類對象的內存管理。如果一個類型需要頻繁的進行對象的創建和釋放,并且對象的創建比較耗時。那么我們可以選擇使用池化技術,預先創建好一定數量的對象,放到對象池中。其實很多池化技術都屬于這個范疇,只是針對不同場景,有其獨特的叫法,如MySQL的連接池、線程池等等。

4.4 棧內存的管理

通常我們只需要管理堆內存,而棧內存交給操作系統來管理。但是棧內存的申請和初始化依舊是由程序員來控制的,而操作系統只負責內存的分配和釋放。考慮如下場景:

void func()
{for (int i = 0; i < 999999; ++i) {char data[1000000];// do something}
}

上述data在每一次循環都需要分配一次內存,然后對其進行操作。如果data的創建可以放到循環體之外進行也不影響程序的正確性。那么data的內存分配只需要一次即可。
事實上,我們還可以繼續優化。因為這塊內存很大,函數也可能會被頻繁調用,每次調用都會重復申請一大塊內存。所以考慮創建一個靜態的data變量,或者靜態的全局變量。只要它能滿足程序的正確性要求。何樂而不為呢?當然靜態變量在多線程環境下會存在數據競爭的問題。此時還可以考慮使用threadlocal變量

五、減少函數調用

5.1 使用內聯函數

一次函數調用涉及兩次指令跳轉。如果一個函數頻繁調用,可以使用內聯機制來避免函數的調用。C++11提供了inline關鍵字,來告訴編譯器被修飾的函數可以在調用處進行替換。一般這樣的函數是一些功能簡單,代碼行較少的函數。當然是否內聯取決于編譯器,inline只是給編譯器建議。是否內聯可通過查看匯編代碼來確認。

5.2 減少函數遞歸調用

函數遞歸通常寫起來比較方便,并且也更好理解。但是函數遞歸帶來的開銷是巨大的,隨著遞歸深度的加深,性能也會受到影響。稍有不慎,甚至會造成棧溢出。所以應該盡量避免使用遞歸函數,考慮使用迭代或者只使用尾遞歸(編譯器優化,只需要當前的棧幀空間,無需開辟新空間)的方式。

六、多線程處理

多線程可以利用多核特性,同時處理多個任務,從而提高程序性能。多線程常常涉及數據競爭的問題,為了提高多線程的性能,應減少數據競爭,常見的方法有:

  • 使用原子變量。
  • 使用讀寫鎖。
  • 降低鎖的持有時間。
  • 使用線程池模型,重復利用線程資源,利用多隊列減少工作線程間的數據競爭。
  • 使用無鎖數據結構,避免頻繁的線程上下文切換。
  • 減少需要共享的狀態。

七、編譯器優化

在考慮性能問題之前,首先得開啟編譯器優化,很多時候,代碼雖然寫的不是最優的,但是在開啟編譯優化之后,往往能達到很好的優化效果。常見的優化選項:

  • O0:不做任何優化。
  • O1:主要對代碼的分支,常量以及表達式等進行優化。
  • O2:會嘗試更多的寄存器級的優化以及指令級的優化。
  • O3:在O2的基礎上進行更多的函數內聯優化,因此目標代碼會更大,但執行速度會更快。

八、指令級優化

8.1 SIMD指令

SIMD(單指令多數據)指的是具有多個處理元件的計算機同時對多個數據執行相同操作的過程。以加法為例,通常情況下,我們需要先取操作數1,再取操作數2,然后求和。而SIMD指令則支持同時取多個操作數,然后執行求和運算。也就是說取操作數的過程是并行的。在gcc中可以通過添加ftree-vectorize編譯選項,或者開啟O2優化,就可以讓編譯器使用SIMD優化代碼。

九、提高緩存命中率

CPU有3級緩存L1、L2、L3,其中L1離核心最近,因此速度也最快。由于緩存有大小限制,因此只有少量的數據和指令會被加載到緩存中,所以經常會出現緩存無法命中的問題。因此提高cache命中率就可以提高程序性能。

9.1 選擇合適的數據結構

數據結構多使用連續的存儲,如vector,而少使用list、map這種非連續存儲類型。因為連續的內存通常會被一起加載到緩存中。

9.2 內存對齊

內存不對齊的情況下,cpu可能需要夸多個內存行去獲取數據。從而增加了cpu的訪存次數,也增加了緩存不命中的可能性。

9.3 減少條件分支

程序中的條件判斷,如 if-else 語句,可以通過邏輯重組或使用分支預測優化來提高緩存命中率。在某些情況下,消除不必要的條件判斷或將其重構為更高效的形式可以減少緩存行的加載和卸載,從而提高性能。例如,使用gcc的提供的關鍵字__builtin_expect來告訴編譯器哪個分支命中的概率最高,從而實現優化。

9.4 其他
  • 避免頻繁的內存分配與釋放,減少內存碎片。
  • 循環展開,減少循環次數。
  • 循環中按行處理數據要比按列處理更高效。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/72240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/72240.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/72240.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Towards Graph Foundation Models: A Survey and Beyond

Towards Graph Foundation Models: A Survey and Beyond WWW24 ?#paper/???#? #paper/&#x1f4a1;#? 背景和動機 背景與意義 隨著基礎模型&#xff08;如大語言模型&#xff09;在NLP等領域的突破&#xff0c;圖機器學習正經歷從淺層方法向深度學習的范式轉變。GFM…

基于 Python 深度學習的電影評論情感分析可視化系統(2.0 全新升級)

基于 Python 深度學習的電影評論情感分析可視化系統&#xff0c;基于 Flask 深度學習&#xff0c;構建了一個 影評情感分析系統&#xff0c;能夠 自動分析影評、計算情感趨勢 并 可視化展示&#xff0c;對于電影行業具有重要參考價值&#xff01; 基于 Python 深度學習的電影評…

Cargo, the Rust package manager, is not installed or is not on PATH.

今天在Windows操作系統上通過pip 安裝jupyter的時候遇到這個報錯&#xff0c;Cargo, the Rust package manager, is not installed or is not on PATH.。 解決辦法 官網&#xff1a;https://rustup.rs/# 下載&#xff1a;https://win.rustup.rs/x86_64 安裝完成之后&#xff0c…

CSS—text文本、font字體、列表list、表格table、表單input、下拉菜單select

目錄 1.文本 2.字體 3.列表list a.無序列表 b.有序列表 c.定義列表 4.表格table a.內容 b.合并單元格 3.表單input a.input標簽 b.單選框 c.上傳文件 4.下拉菜單 1.文本 屬性描述color設置文本顏色。direction指定文本的方向 / 書寫方向。letter-spacing設置字符…

開啟AI短劇新紀元!SkyReels-V1/A1雙劍合璧!昆侖萬維開源首個面向AI短劇的視頻生成模型

論文鏈接&#xff1a;https://arxiv.org/abs/2502.10841 項目鏈接&#xff1a;https://skyworkai.github.io/skyreels-a1.github.io/ Demo鏈接&#xff1a;https://www.skyreels.ai/ 開源地址&#xff1a;https://github.com/SkyworkAI/SkyReels-A1 https://github.com/Skywork…

數學建模:MATLAB極限學習機解決回歸問題

一、簡述 極限學習機是一種用于訓練單隱層前饋神經網絡的算法&#xff0c;由輸入層、隱藏層、輸出層組成。 基本原理&#xff1a; 輸入層接受傳入的樣本數據。 在訓練過程中隨機生成從輸入層到隱藏層的所有連接權重以及每個隱藏層神經元的偏置值&#xff0c;這些參數在整個…

Android15音頻進階之定位混音線程丟幀問題(一百零八)

簡介: CSDN博客專家、《Android系統多媒體進階實戰》一書作者 新書發布:《Android系統多媒體進階實戰》?? 優質專欄: Audio工程師進階系列【原創干貨持續更新中……】?? 優質專欄: 多媒體系統工程師系列【原創干貨持續更新中……】?? 優質視頻課程:AAOS車載系統+…

_ 為什么在python中可以當變量名

在 Python 中&#xff0c;_&#xff08;下劃線&#xff09;是一個有效的變量名&#xff0c;這主要源于 Python 的命名規則和一些特殊的使用場景。以下是為什么 _ 可以作為變量名的原因和常見用途&#xff1a; --- ### 1. **Python 的命名規則** Python 允許使用字母&#xff…

Electron+Vite+React+TypeScript開發問題手冊

ElectronViteReactTypeScript跨平臺開發全問題手冊 一、開發環境配置類問題 1.1 依賴安裝卡頓&#xff08;國內網絡環境&#xff09; 問題現象&#xff1a;執行npm install時卡在node-gyp編譯或Electron二進制包下載階段 解決方案&#xff1a; # 配置國內鏡像源 npm config …

【計算機網絡入門】初學計算機網絡(七)

目錄 1. 滑動窗口機制 2. 停止等待協議&#xff08;S-W&#xff09; 2.1 滑動窗口機制 2.2 確認機制 2.3 重傳機制 2.4 為什么要給幀編號 3. 后退N幀協議&#xff08;GBN&#xff09; 3.1 滑動窗口機制 3.2 確認機制 3.3 重傳機制 4. 選擇重傳協議&#xff08;SR&a…

《Python實戰進階》No 8:部署 Flask/Django 應用到云平臺(以Aliyun為例)

第8集&#xff1a;部署 Flask/Django 應用到云平臺&#xff08;以Aliyun為例&#xff09; 2025年3月1日更新 增加了 Ubuntu服務器安裝Python詳細教程鏈接。 引言 在現代 Web 開發中&#xff0c;開發一個功能強大的應用只是第一步。為了讓用戶能夠訪問你的應用&#xff0c;你需…

GitLab Pages 托管靜態網站

文章目錄 新建項目配置博客添加 .gitlab-ci.yml其他配置 曾經用 Github Pages 來托管博客內容&#xff0c;但是有一些不足&#xff1a; 在不科學上網的情況下&#xff0c;是沒法訪問的&#xff0c;或者訪問速度非常慢代碼倉庫必須是公開的&#xff0c;如果設置為私有&#xff0…

TVbox蜂蜜影視:智能電視觀影新選擇,簡潔界面與強大功能兼具

蜂蜜影視是一款基于貓影視開源項目 CatVodTVJarLoader 開發的智能電視軟件&#xff0c;專為追求簡潔與高效觀影體驗的用戶設計。該軟件從零開始編寫&#xff0c;界面清爽&#xff0c;操作流暢&#xff0c;特別適合在智能電視上使用。其最大的亮點在于能夠自動跳過失效的播放地址…

形象生動講解Linux 虛擬化 I/O

用現實生活的比喻和簡單例子來解釋 Linux 虛擬化 I/O&#xff0c;就像給朋友講故事一樣。 虛擬化 I/O 要解決什么問題&#xff1f; 想象你有一棟大房子&#xff08;物理服務器&#xff09;&#xff0c;想把它分割成多個小公寓&#xff08;虛擬機&#xff09;出租。每個租客&…

Java內存管理與性能優化實踐

Java內存管理與性能優化實踐 Java作為一種廣泛使用的編程語言&#xff0c;其內存管理和性能優化是開發者在日常工作中需要深入了解的重要內容。Java的內存管理機制借助于垃圾回收&#xff08;GC&#xff09;來自動處理內存的分配和釋放&#xff0c;但要實現高效的內存管理和優…

代碼隨想錄算法訓練營第三十天 | 卡碼網46.攜帶研究材料(二維解法)、卡碼網46.攜帶研究材料(滾動數組)、LeetCode416.分割等和子集

代碼隨想錄算法訓練營第三十天 | 卡碼網46.攜帶研究材料&#xff08;二維解法&#xff09;、卡碼網46.攜帶研究材料&#xff08;滾動數組&#xff09;、LeetCode416.分割等和子集 01-1 卡碼網46.攜帶研究材料&#xff08;二維&#xff09; 相關資源 題目鏈接&#xff1a;46. 攜…

nvidia驅動更新,centos下安裝openwebui+ollama(非docker)

查看centos內核版本 uname -a cat /etc/redhat-release下載對應的程序&#xff08;這個是linux64位版本通用的&#xff09; https://cn.download.nvidia.cn/tesla/550.144.03/NVIDIA-Linux-x86_64-550.144.03.run cudnn想辦法自己下一下&#xff0c;我這里是12.x和11.x通用的…

【AIGC系列】4:Stable Diffusion應用實踐和代碼分析

AIGC系列博文&#xff1a; 【AIGC系列】1&#xff1a;自編碼器&#xff08;AutoEncoder, AE&#xff09; 【AIGC系列】2&#xff1a;DALLE 2模型介紹&#xff08;內含擴散模型介紹&#xff09; 【AIGC系列】3&#xff1a;Stable Diffusion模型原理介紹 【AIGC系列】4&#xff1…

51單片機-串口通信編程

串行口工作之前&#xff0c;應對其進行初始化&#xff0c;主要是設置產生波特率的定時器1、串行口控制盒中斷控制。具體步驟如下&#xff1a; 確定T1的工作方式&#xff08;編程TMOD寄存器&#xff09;計算T1的初值&#xff0c;裝載TH1\TL1啟動T1&#xff08;編程TCON中的TR1位…

Windows 10 遠程桌面連接使用指南

目錄 一、引言 二、準備工作 1、確認系統版本 2、服務器端設置 三、客戶端連接 1、打開遠程桌面連接程序 2、輸入連接信息 3、輸入登錄憑證 4、開始使用遠程桌面 四、移動端連接&#xff08;以 iOS 為例&#xff09; 1、下載安裝應用 2、添加遠程計算機 3、進行連接…