標準庫 `strlen` 效能遠超朴素逐字節循環。GLIBC 採用**字長級並行檢測**(一次檢查 4/8 字節),結合**記憶體對齊預處理**、**魔數位運算**,實現 O(N/word_size) 掃描。本文逐行解析 GLIBC 2.x `strlen.c` 核心邏輯,揭示其高效原理。
設 `lomagic = 0x01010101`,`himagic = 0x80808080`。
對任意字長 `x`,計算 `((x - lomagic) & ~x) & himagic`:
**結論**:表達式非零 ⇒ **至少一個字節為 0** (或 0x80 誤報)。
size_t strlen(const char *str) {
const char *char_ptr = str;
const unsigned long *longword_ptr;
unsigned long longword, himagic, lomagic;
// 1. 對齊預處理:逐字節直到地址 % sizeof(long) == 0
for (; ((unsigned long)char_ptr & (sizeof(longword) - 1)) != 0; ++char_ptr)
if (*char_ptr == '\0') return char_ptr - str;
// 2. 設置魔數
longword_ptr = (const unsigned long*)char_ptr;
himagic = 0x80808080L;
lomagic = 0x01010101L;
if (sizeof(longword) > 4) { // 64 位擴展
himagic = (himagic << 32) | himagic;
lomagic = (lomagic << 32) | lomagic;
}
// 3. 主循環:字長級掃描
for (;;) {
longword = *longword_ptr++; // 載入 4/8 字節
// 4. 零字節檢測
if ((((longword - lomagic) & ~longword) & himagic) != 0) {
// 5. 命中:退回逐字節精確定位
const char *cp = (const char*)(longword_ptr - 1);
if (cp[0] == 0) return cp - str;
if (cp[1] == 0) return cp - str + 1;
if (cp[2] == 0) return cp - str + 2;
if (cp[3] == 0) return cp - str + 3;
if (sizeof(longword) > 4) {
if (cp[4] == 0) return cp - str + 4;
if (cp[5] == 0) return cp - str + 5;
if (cp[6] == 0) return cp - str + 6;
if (cp[7] == 0) return cp - str + 7;
}
}
}
}
| 字符串長度 | GLIBC strlen (ms) | 朴素實現 (ms) | 加速比 |
|---|---|---|---|
| 10 | 32,762 | 491,836 | 15x |
| 20 | 35,075 | 770,397 | 22x |
**關鍵觀察**:GLIBC 耗時隨長度增長極緩慢(主要耗在對齊預處理),朴素實現線性增長。
GLIBC 會在編譯時根據目標架構選擇最優實現(`sysdeps/x86_64/strlen.S` 等)。