代碼使用 hash_map(或 unordered_map<string, double>)存儲約 1,000 萬條鍵值對(Key 為 6 位字符串,Value 為 double),運行至約 1,100 萬條時拋出 std::bad_alloc 或進程被 OOM Killer 終止。
| 組件 | 單條估算 | 1,000 萬條總計 |
|---|---|---|
| string 對象(SSO 短字符串優化,約 32 字節) | ~32 B | ~320 MB |
| double 值 | 8 B | ~80 MB |
| 哈希桶數組(負載因子 1.0,指針 8 B) | 8 B | ~80 MB |
| 節點元數據(指針、哈希值、對齊填充,約 24 B) | ~24 B | ~240 MB |
| 分配器元數據/碎片 | ~20% | ~140 MB |
| 總計 | ~84 B | ~860 MB+ |
加上程序本身、庫、系統開銷,輕鬆超過 1.5~2 GB。32 位進程地址空間僅 2~3 GB,64 位雖理論無上限,但物理內存+Swap 耗盡同樣會崩潰。
Key 僅包含 '0'~'3' 四字符,長度固定 6,共 4^6 = 4096 種可能。可將 Key 編碼為 12 位二進制整數(2 bits/char × 6 = 12 bits),直接用 uint16_t 作為數組下標。
// 編碼函數
uint16_t encodeKey(const string& s) {
uint16_t code = 0;
for (char c : s) code = (code << 2) | (c - '0'); // 假設字符僅 '0'~'3'
return code;
}
// 緊湊存儲
const size_t MAX_KEYS = 1 << 12; // 4096
vector compactMap(MAX_KEYS, 0.0); // 初始化默認值
// 插入/查找 O(1),無哈希開銷,內存 < 4096 * 8B = 32 KB
compactMap[encodeKey(key)] = value;
若 Key 範圍較大但稀疏,可用 robin_hood_hashing 庫(如 tsl::robin_map、ska::flat_hash_map),內存比標準庫節省 30%~50%。
mmap 大文件,自定義哈希索引結構,突破進程地址空間限制。unordered_map mp;
mp.reserve(12'000'000); // 預分配桶,避免多次重哈希
mp.max_load_factor(0.7); // 降低負載因子,減少碰撞
// 若 Key 可預先生成,考慮用 string_view 或 char[7] 避免 string 分配
CharsAllSequen 遞歸生成 Key 時是否產生大量臨時 string/stringstream,導致內存碎片。改用字符數組 + 索引遞歸,僅在葉子節點構造最終 Key。heaptrack / valgrind --tool=massif 確認內存增長曲線,定位峰值分配點。總結:業務 Key 域極小(4096)時,直接用數組索引是極致優化;通用大規模 KV 落地首選 RocksDB 等 LSM-Tree 引擎,避免在通用哈希表上硬抗內存壓力。