優化效果遞減順序:
算法/資料結構 > 編譯器優化 > 代碼微調 > 組語手寫
本文聚焦「代碼微調」層級,假設算法已合理、編譯器已開啟 -O2/-Os。
| 場景 | 低效寫法 | 優化方案 | 複雜度改善 |
|---|---|---|---|
| 查找 | 線性 O(N) | 二分 O(log N) / 雜湊 O(1) | 指數級 |
| 排序 | 冒泡/插入 O(N²) | 快速/堆排/歸併 O(N log N) | 多項式級 |
| 頻繁插入刪除 | 陣列位移 | 雙向鏈表 / 平衡樹 | O(N)→O(1)/O(log N) |
| 字串匹配 | 朴素 O(NM) | KMP / Boyer-Moore / AC 自動機 | 線性化 |
原則:先優化算法,再優化代碼。
uint8_t 代替 int 存 0~255,節省 RAM、減少記憶體頻寬、向量化更易。float / double,定點運算(Q15/Q31)在無 FPU 的 MCU 上快 10~100 倍。printf 格式:%d < %ld << %f(浮點庫極大)。嵌入式建議自製 itoa/u32_to_str 避免引入 stdio 巨大符號表。早期 8/16 位 MCU(8051, AVR, MSP430)指標確實更快。現代 ARM Cortex-M / RISC-V 編譯器對陣列索引優化極佳,實測為準。關鍵循環可嘗試:
/* 陣列版 */
for (i=0; i
對比反組譯碼與週期數再決定。
/* 建議 */
++i; i += 1; i -= 1;
/* 避免 */
i = i + 1; i = i - 1;
-O2 通常自動轉換,但 -O0 除錯構建保留差異,養成習慣無害。
| 原運算 | 等價替換 | 條件 | 說明 |
|---|---|---|---|
x % 8 | x & 7 | 2 的冪次 | 位元 AND 取代除法/取模 |
x * 4 | x << 2 | 2 的冪次 | 移位取代乘法 |
x / 4 | x >> 2 | 無符號/正數 | 算術右移保留符號 |
pow(x,2) | x*x | 整數/定點 | 避免庫函數開銷 |
x * 9 | (x<<3) + x | 常數乘法 | 移位+加法組合 |
sqrt(x) | 查表/牛頓迭代 | 嵌入式 | 查表 256/1024 項足夠 |
/* 慢 */
for (i=0; i
/* 正向 */
for (i=0; i<1000; ++i) { ... }
/* 逆向:比較 0 通常單指令 */
for (i=1000; i--; ) { ... } // i 從 999 降到 0
/* 或 */
i = 1000;
do { ... } while (--i);
注意:迴圈內若用 i 索引陣列,逆向會改變存取順序,需確認語意不變。
do-while vs whiledo-while 保證執行一次,結尾單一分支,代碼較緊湊。
while 需入口判斷 + 迴圈末尾跳轉,兩條分支。
對極小迴圈體,do-while 可省 1~2 位元組。
sin/cos 256/1024 項線性插值。const 區(ROM),__attribute__((section(".rodata"))) 確保不佔 RAM。const char msg[] = "..." 避免複製到 RAM。static inline 或宏。struct { uint8_t a:3, b:5; } 壓縮狀態機狀態。__attribute__((hot))、(cold)、(pure)、(const) 引導優化。-O2 -march=native 或目標 MCU 旗標)。git commit + 基準測試對比。代碼大小(.text/.data)、執行週期、RAM 峰值、功耗。NDEBUG?優化是度量驅動的工程活動,而非經驗主義的猜測遊戲。建立基準、測量、對比、文檔化,讓每一次微調都有據可查。