C++性能優化指南


C++性能優化指南

O'Reilly Media, Inc. 介紹

1 優化概述

1.1 優化是軟件開發的一部分

1.2 優化是高效的

1.3 優化是沒有問題的

1.4 這兒一納秒,那兒一納秒

1.5 C++ 代碼優化策略總結

1.5.1 用好的編譯器並用好編譯器
1.5.2 使用更好的算法
1.5.3 使用更好的庫
1.5.4 減少內存分配和復制
1.5.5 移除計算
1.5.6 使用更好的數據結構
1.5.7 提高並發性
1.5.8 優化內存管理

1.6 小結

2 影響優化的計算機行為

2.1 C++ 所相信的計算機謊言

2.2 計算機的真相

2.2.1 內存很慢
2.2.2 內存訪問並非以字節為單位
2.2.3 某些內存訪問會比其他的更慢
2.2.4 內存字分為大端和小端
2.2.5 內存容量是有限的
2.2.6 指令執行緩慢
2.2.7 計算機難以作決定
2.2.8 程序執行中的多個流
2.2.9 調用操作系統的開銷是昂貴的

2.3 C++ 也會說謊

2.3.1 並非所有語句的性能開銷都相同
2.3.2 語句並非按順序執行

2.4 小結

3 測量性能

3.1 優化思想

3.1.1 必須測量性能
3.1.2 優化器是王牌獵人
3.1.3 90/10規則
3.1.4 阿姆達爾定律

3.2 進行實驗

3.2.1 記實驗筆記
3.2.2 測量基准性能並設定目標
3.2.3 你只能改善你能夠測量的

3.3 分析程序執行

3.4 測量長時間運行的代碼

3.4.1 一點關於測量時間的知識
3.4.2 用計算機測量時間
3.4.3 克服測量障礙
3.4.4 創建 stopwatch 類
3.4.5 使用測試套件測量熱點函數

3.5 評估代碼開銷來找出熱點代碼

3.5.1 評估獨立的 C++ 語句的開銷
3.5.2 評估循環的開銷

3.6 其他找出熱點代碼的方法

3.7 小結

4 優化字符串的使用:案例研究

4.1 為什么字符串很麻煩

4.1.1 字符串是動態分配的
4.1.2 字符串就是值
4.1.3 字符串會進行大量復制

4.2 第一次嘗試優化字符串

4.2.1 使用復合賦值操作避免臨時字符串
4.2.2 通過預留存儲空間減少內存的重新分配
4.2.3 消除對參數字符串的復制
4.2.4 使用迭代器消除指針解引
4.2.5 消除對返回的字符串的復制
4.2.6 用字符數組代替字符串
4.2.7 第一次優化總結

4.3 第二次嘗試優化字符串

4.3.1 使用更好的算法
4.3.2 使用更好的編譯器
4.3.3 使用更好的字符串庫
4.3.4 使用更好的內存分配器

4.4 消除字符串轉換

4.4.1 將 C 字符串轉換為 std::string
4.4.2 不同字符集間的轉換

4.5 小結

5 優化算法

5.1 算法的時間開銷

5.1.1 最優情況、平均情況和最差情況的時間開銷
5.1.2 攤銷時間開銷
5.1.3 其他開銷

5.2 優化查找和排序的工具箱

5.3 高效查找算法

5.3.1 查找算法的時間開銷
5.3.2 當 n 很小時,所有算法的時間開銷都一樣

5.4 高效排序算法

5.4.1 排序算法的時間開銷
5.4.2 替換在最差情況下性能較差的排序算法
5.4.3 利用輸入數據集的已知特性

5.5 優化模式

5.5.1 預計算
5.5.2 延遲計算
5.5.3 批量處理
5.5.4 緩存
5.5.5 特化
5.5.6 提高處理量
5.5.7 提示
5.5.8 優化期待路徑
5.5.9 散列法
5.5.10 雙重檢查

5.6 小結

6 優化動態分配內存的變量

6.1 C++ 變量回顧

6.1.1 變量的存儲期
6.1.2 變量的所有權
6.1.3 值對象與實體對象

6.2 C++ 動態變量 API 回顧

6.2.1 使用智能指針實現動態變量所有權的自動化
6.2.2 動態變量有運行時開銷

6.3 減少動態變量的使用

6.3.1 靜態地創建類實例
6.3.2 使用靜態數據結構
6.3.3 使用 std::make_shared 替代 new 表達式
6.3.4 不要無謂地共享所有權
6.3.5 使用“主指針”擁有動態變量

6.4 減少動態變量的重新分配

6.4.1 預分配動態變量以防止重新分配
6.4.2 在循環外創建動態變量

6.5 移除無謂的復制

6.5.1 在類定義中禁止不希望發生的復制
6.5.2 移除函數調用上的復制
6.5.3 移除函數返回上的復制
6.5.4 免復制庫
6.5.5 實現寫時復制慣用法
6.5.6 切割數據結構

6.6 實現移動語義

6.6.1 非標准復制語義:痛苦的實現
6.6.2 std::swap() :“窮人”的移動語義
6.6.3 共享所有權的實體
6.6.4 移動語義的移動部分
6.6.5 更新代碼以使用移動語義
6.6.6 移動語義的微妙之處

6.7 扁平數據結構

6.8 小結

7 優化熱點語句

7.1 從循環中移除代碼

7.1.1 緩存循環結束條件值
7.1.2 使用更高效的循環語句
7.1.3 用遞減替代遞增
7.1.4 從循環中移除不變性代碼
7.1.5 從循環中移除無謂的函數調用
7.1.6 從循環中移除隱含的函數調用
7.1.7 從循環中移除昂貴的、緩慢改變的調用
7.1.8 將循環放入函數以減少調用開銷
7.1.9 不要頻繁地進行操作
7.1.10 其他優化技巧

7.2 從函數中移除代碼

7.2.1 函數調用的開銷
7.2.2 簡短地聲明內聯函數
7.2.3 在使用之前定義函數
7.2.4 移除未使用的多態性
7.2.5 放棄不使用的接口
7.2.6 用模板在編譯時選擇實現
7.2.7 避免使用 PIMPL 慣用法
7.2.8 移除對 DDL 的調用
7.2.9 使用靜態成員函數取代成員函數
7.2.10 將虛析構函數移至基類中

7.3 優化表達式

7.3.1 簡化表達式
7.3.2 將常量組合在一起
7.3.3 使用更高效的運算符
7.3.4 使用整數計算替代浮點型計算
7.3.5 雙精度類型可能會比浮點型更快
7.3.6 用閉形式替代迭代計算

7.4 優化控制流程慣用法

7.4.1 用 switch 替代 if-else if-else
7.4.2 用虛函數替代 switch 或 if
7.4.3 使用無開銷的異常處理

7.5 小結

8 使用更好的庫

8.1 優化標准庫的使用

8.1.1 C++ 標准庫的哲學
8.1.2 使用 C++ 標准庫的注意事項

8.2 優化現有庫

8.2.1 改動越少越好
8.2.2 添加函數,不要改動功能

8.3 設計優化庫

8.3.1 草率編碼后悔多
8.3.2 在庫的設計上,簡約是一種美德
8.3.3 不要在庫內分配內存
8.3.4 若有疑問,以速度為准
8.3.5 函數比框架更容易優化
8.3.6 扁平繼承層次關系
8.3.7 扁平調用鏈
8.3.8 扁平分層設計
8.3.9 避免動態查找
8.3.10 留意“上帝函數”

8.4 小結

9 優化查找和排序

9.1 使用 std::map 和 std::string 的鍵值對表

9.2 改善查找性能的工具箱

9.2.1 進行一次基准測量
9.2.2 識別出待優化的活動
9.2.3 分解待優化的活動
9.2.4 修改或替換算法和數據結構
9.2.5 在自定義抽象上應用優化過程

9.3 優化 std::map 的查找

9.3.1 以固定長度的字符數組作為 std::map 的鍵
9.3.2 以 C 風格的字符串組作為鍵使用 std::map
9.3.3 當鍵就是值的時候,使用 map 的表親 std::set

9.4 使用 頭文件優化算法

9.4.1 以序列容器作為被查找的鍵值對表
9.4.2 std::find() :功能如其名,O(n) 時間開銷
9.4.3 std::binary_search() :不返回值
9.4.4 使用 std::equal_range() 的二分查找
9.4.5 使用 std::lower_bound() 的二分查找
9.4.6 自己編寫二分查找法
9.4.7 使用 strcmp() 自己編寫二分查找法

9.5 優化鍵值對散列表中的查找

9.5.1 使用 std::unordered_map 進行散列
9.5.2 對固定長度字符數組的鍵進行散列
9.5.3 以空字符結尾的字符串為鍵進行散列
9.5.4 用自定義的散列表進行散列

9.6 斯特潘諾夫3的抽象懲罰

9.7 使用 C++ 標准庫優化排序

9.8 小結

10 優化數據結構

10.1 理解標准庫容器

10.1.1 序列容器
10.1.2 關聯容器
10.1.3 測試標准庫容器

10.2 std::vector 與 std::string

10.2.1 重新分配的性能影響
10.2.2 std::vector 中的插入與刪除
10.2.3 遍歷 std::vector
10.2.4 對 std::vector 排序
10.2.5 查找 std::vector

10.3 std::deque

10.3.1 std::deque 中的插入和刪除
10.3.2 遍歷 std::deque
10.3.3 對 std::deque 的排序
10.3.4 查找 std::deque

10.4 std::list

10.4.1 std::list 中的插入和刪除
10.4.2 遍歷 std::list 中
10.4.3 對 std::list 排序
10.4.4 查找 std::list

10.5 std::forward_list

10.5.1 std::forward_list 中的插入和刪除
10.5.2 遍歷 std::forward_list
10.5.3 對 std::forward_list 排序
10.5.4 查找 std::forward_list

10.6 std::map 與 std::multimap

10.6.1 std::map 中的插入和刪除
10.6.2 遍歷 std::map
10.6.3 對 std::map 排序
10.6.4 查找 std::map

10.7 std::set 與 std::multiset

10.8 std::unordered_map 與 std::unordered_multimap

10.8.1 std::unordered_map 中的插入與刪除
10.8.2 遍歷 std::unordered_map
10.8.3 查找 std::unordered_map

10.9 其他數據結構

10.10 小結

11 優化 I/O

11.1 讀取文件的秘訣

11.1.1 創建一個吝嗇的函數簽名
11.1.2 縮短調用鏈
11.1.3 減少重新分配
11.1.4 更大的吞吐量——使用更大的輸入緩沖區
11.1.5 更大的吞吐量——一次讀取一行
11.1.6 再次縮短函數調用鏈
11.1.7 無用的技巧

11.2 寫文件

11.3 從 std::cin 讀取和向 std::cout 中寫入

11.4 小結

12 優化並發

12.1 復習並發

12.1.1 並發概述
12.1.2 交叉執行
12.1.3 順序一致性
12.1.4 競爭
12.1.5 同步
12.1.6 原子性

12.2 復習 C++ 並發方式

12.2.1 線程
12.2.2 promise 和 future
12.2.3 異步任務
12.2.4 互斥量
12.2.5 鎖
12.2.6 條件變量
12.2.7 共享變量上的原子操作
12.2.8 展望未來的 C++ 並發特性

12.3 優化多線程 C++ 程序

12.3.1 用 std::async 替代 std::thread
12.3.2 創建與核心數量一樣多的可執行線程
12.3.3 實現任務隊列和線程池
12.3.4 在單獨的線程中執行 I/O
12.3.5 沒有同步的程序
12.3.6 移除啟動和停止代碼

12.4 讓同步更加高效

12.4.1 減小臨界區的范圍
12.4.2 限制並發線程的數量
12.4.3 避免驚群
12.4.4 避免鎖護送
12.4.5 減少競爭
12.4.6 不要在單核系統上繁忙等待
12.4.7 不要永遠等待
12.4.8 自己設計互斥量可能會低效
12.4.9 限制生產者輸出隊列的長度

12.5 並發庫

12.6 小結

13 優化內存管理

13.1 復習 C++ 內存管理器 API

13.1.1 動態變量的生命周期
13.1.2 內存管理函數分配和釋放內存
13.1.3 new 表達式構造動態變量
13.1.4 delete 表達式處置動態變量
13.1.5 顯式析構函數調用銷毀動態變量

13.2 高性能內存管理器

13.3 提供類專用內存管理器

13.3.1 分配固定大小內存的內存管理器
13.3.2 內存塊分配區
13.3.3 添加一個類專用 new() 運算符
13.3.4 分配固定大小內存塊的內存管理器的性能
13.3.5 分配固定大小內存塊的內存管理器的變化形式
13.3.6 非線程安全的內存管理器是高效的

13.4 自定義標准庫分配器

13.4.1 最小 C++11分配器
13.4.2 C++98分配器的其他定義
13.4.3 一個分配固定大小內存塊的分配器
13.4.4 字符串的分配固定大小內存塊的分配器

13.5 小結

作者介紹

封面介紹

看完了

思維導圖

C++性能優化指南

防止博客圖床圖片失效,防止圖片源站外鏈:

http://www.processon.com/chart_image/5e5b3fc5e4b03627650b1f45.png)

思維導圖在線編輯鏈接:

https://www.processon.com/view/5e5b3fc5e4b03627650b1f42


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM