20% 的代碼消耗 80% 運行時間。優化重點應集中在熱點路徑,而非平均用力。
// 方法 A:運行時構建字符串
#define LEN 32
char string1[LEN];
memset(string1, 0, LEN);
strcpy(string1, "This is a example!!");
// 方法 B:編譯期常量 + 指針引用
const char string2[LEN] = "This is a example!";
char *cp = string2; // 直接使用,零開銷
適用場景:高頻調用、字符串內容固定、內存充裕。缺點:靈活性降低,需預存所有變體。
// 函數版:有調用開銷(棧操作、現場保存)
int BIT_MASK(int __bf) {
return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
}
// 宏版:零調用開銷,代碼內聯展開
#define BIT_MASK(__bf) \
(((1U << (bw ## __bf)) - 1) << (bs ## __bf))
#define SET_BITS(__dst, __bf, __val) \
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \
(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
ARM 官方源碼採用宏實現位操作,極致精簡。權衡:代碼膨脹 vs 調用開銷。
// 方法 E:循環累加 1~100
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i; // 100 次循環
// 方法 F:等差數列求和公式
int sum = (100 * (1 + 100)) / 2; // 1 次乘法、1 次加法、1 次除法
核心思想:發掘數學規律,將 O(N) 降為 O(1)。編程前多思考:有無閉式解?
// 除以 2 的冪次 → 右移
int div8 = 257 / 8; // 調用除法指令
int div8_opt = 257 >> 3; // 單條移位指令
// 模 2 的冪次 → 位與
int mod32 = 456 % 32; // 調用取模指令
int mod32_opt = 456 & (32 - 1); // 456 & 0x1F,單條 AND 指令
// 通用技巧:x % (2^n) == x & (2^n - 1)
乘除指令週期通常遠大於移位/邏輯指令。嵌入式中位操作更是控制寄存器的基礎技能。
// 數組拷貝:C 版 1024 次循環
for (int i = 0; i < 1024; i++)
string2[i] = string1[i];
// ARM 內聯匯編:128 次循環,每次 LDMIA/STMIA 傳 8 字
__asm {
MOV R0, string1
MOV R1, string2
MOV R2, #0
loop:
LDMIA R0!, {R3-R10} // 批量加載 8 字
STMIA R1!, {R3-R10} // 批量存儲 8 字
ADD R2, R2, #8
CMP R2, #1024
BNE loop
}
代價:喪失可移植性、違背軟件工程原則、維護極難。僅在極端性能敏感且無他法時使用。
WORD Addition(BYTE n) {
register int i, s = 0; // 建議放入寄存器
for (i = 1; i <= n; i++)
s += i;
return s;
}
register 是「建議」而非強制| 層級 | 訪問速度 | 優化策略 |
|---|---|---|
| CPU 內部 RAM | 最快 | 關鍵熱點數據放這裡 |
| 外部同步 RAM | 快 | 代碼段拷貝至此運行 |
| 外部異步 RAM | 中等 | 大數組、緩衝區 |
| FLASH/ROM | 最慢 | 啟動時拷貝代碼到 RAM |
記住:過早優化是萬惡之源。先寫正確、清晰的代碼,再針對瓶頸優化。