優化不是「寫花哨技巧」,而是**在正確性前提下,權衡代碼體積、執行速度、內存佔用、可維護性的系統工程**。本文從**程序結構、數據類型、算法選擇、表達式重寫、循環變換、編譯器協作、內存訪問**七個維度,給出可落地的優化清單與避坑指南。
一、程序結構優化:可讀性是優化的前提
- 模塊化與分層:單一職責函數,
.h/.c 分離,降低耦合,便於單元測試與編譯器內聯優化。
- 標識符語義化:`user_count` 優於 `cnt`,`MAX_RETRY` 優於 `10`,減少認知負荷與維護 Bug。
- 常量宏化:`#define BUF_SIZE 1024` 統一維護,避免魔數散落。
- 減少全局變量:優先局部變量(寄存器分配友好),全局變量加 `static` 限制作用域,必要時用 `volatile`/`_Atomic`。
- 條件編譯替代運行時判斷:`#ifdef DEBUG` 輸出日誌,發布版零開銷。
二、數據類型選擇:越小越快
| 原則 | 示例 | 收益 |
能用 uint8_t 不用 int | 標志位、狀態碼、小範圍計數 | 節省內存、向量化友好、緩存命中率↑ |
能用 int32_t 不用 int64_t | 一般整數運算 | 32 位平台單指令,64 位需雙指令 |
| 避免浮點 | 定點數 Q16.16、整數縮放 | 無 FPU 時軟件模擬極慢;有 FPU 也比整數慢 |
| 位域壓縮標志組 | `struct { unsigned a:1; b:3; c:4; }` | 多標志合並為一字節/字 |
三、算法與數據結構:降維打擊
- 查找:
O(n) 線性 → O(log n) 二分/樹 → O(1) 哈希/完美哈希。
- 排序:
O(n²) 冒泡/插入 → O(n log n) 快排/歸併/堆排 → O(n) 基數/計數排序(定域整數)。
- 頻繁插刪:數組 → 鏈表/雙向鏈表/跳表/B+樹。
- 字符串匹配:朴素 → KMP/BM/AC 自動機。
四、表達式重寫:編譯器看不懂的優化機會
| 模式 | 優化前 | 優化後 | 原理 |
| 求模 2ⁿ | `a % 8` | `a & 7` | 位運算單周期,取模需除法指令/庫調用 |
| 平方/立方 | `pow(x,2)` / `pow(x,3)` | `x*x` / `x*x*x` | 硬件乘法 1-3 周期,pow 庫函數數百周期 |
| 乘除 2ⁿ | `x * 8` / `x / 8` | `x << 3` / `x >> 3` | 移位指令極快,乘除需硬件乘除單元 |
| 乘常數 | `x * 9` | `(x << 3) + x` | 移位+加法替代乘法,部分無乘法器 MCU 必用 |
| 自增自減 | `i = i + 1` | `++i` / `i++` | 編譯器易生成 `inc/dec`,避免 `load-add-store` |
| 復合賦值 | `a = a + b` | `a += b` | 同左值單次求值,減少臨時變量 |
五、循環變換:熱點代碼的主戰場
- 循環不變量外提:函數調用、指釖運算、數組基址計算、常量表達式 → 移至循環外。
- 倒序循環利用零標志:
// 正序:需 cmp+branch
for (i=0; i
- 循環展開:手寫或 `#pragma unroll`,減少分支預測失敗,增加指令級並行(ILP)。
- 循環融合/分發:合併遍歷同一數組的多個循環(融合),或拆分獨立計算以便向量化(分發)。
- 避免循環內分配內存:池化、棧上分配、預分配緩衝區。
六、編譯器協作:讓優化器成為盟友
| 編譯器 | 關鍵優化選項 | 建議 |
| GCC/Clang | -O2 -pipe -fomit-frame-pointer -march=native | 發布版標配;`-O3` 可能增大體積且稀有收益 |
| MSVC | /O2 /GL /LTCG /arch:AVX2 | 全程序優化 (LTCG) 鏈接時跨模塊內聯 |
| ICC | -O3 -xHost -ipo -qopt-report=5 | 查看優化報告確認向量化/內聯情況 |
- 關鍵屬性標註:`__attribute__((hot))`、`__attribute__((const))`、`__attribute__((pure))`、`__builtin_expect`、`restrict` 指針無別名。
- Profile-Guided Optimization (PGO):`-fprofile-generate` 訓練運行 → `-fprofile-use` 針對性優化,熱點路徑極大收益。
七、內存訪問優化:緩存是性能之匙
- 結構體字段重排:熱字段聚合、按對齊要求排序、避免偽共享(緩存行填充)。
- 數組結構體 (AoS) → 結構體數組 (SoA):向量化加載連續字段,SIMD 吞吐最大化。
- 預取:`__builtin_prefetch(ptr)` 或硬件預取友好訪問模式(順序掃描)。
- 內存池/對象池:高頻小對象分配,消除 `malloc/free` 開銷與碎片。
避坑紅線:過早優化是萬惡之源
- 先 Profile,後優化:`perf top` / `gprof` / `VTune` / `Visual Studio Profiler` 定位熱點,**只優化前 20% 佔 80% 時間的代碼**。
- 保留可讀版本:優化後代碼加詳細註釋,保留未優化版本作為參考/回滾基準。
- 警惕 UB 借優化作惡:有符號溢出、空指針解引用、移位超寬度等 UB 允許編譯器刪除代碼,**嚴禁利用 UB 優化**。
- 嵌入式特殊約束:代碼體積受限(`-Os`)、無 FPU(定點數)、無 MMU(靜態分配)、實時性(WCET 分析)。
優化是**度量驅動的工程決策**,而非技巧堆砌。建立「測量 → 分析 → 單一變量優化 → 回歸測試」閉環,才能在不引入 Bug 的前提下,榨乾硬件的每一分算力。