缓存未必加速,轻量递归或低重复调用时std::unordered_map开销可能超原函数;须先测耗时、析参数分布、验副作用,再决策缓存。

直接上缓存不一定快,尤其对轻量递归或低重复率调用,std::unordered_map 的哈希、内存分配、查找跳转开销可能比原函数还重;先测耗时、看参数分布、确认副作用,再决定要不要缓。
缓存前必须确认的三件事
不加判断就套 memoize 模板,很容易让程序变慢而不是变快:
- 用
std::chrono::high_resolution_clock测原函数单次耗时:若平均 - 打印实际入参(临时加
std::cout ):确认是否存在高频重复(如 <code>fib(20)被调上百次),否则缓存只是占内存 - 检查函数是否有副作用:含
rand()、time(nullptr)、std::cin、全局变量修改等,缓存后结果会“假一致”
数组缓存 vs 哈希表缓存:选哪个?
关键看参数是否密集、范围是否可控:
- 参数是
int n且n ∈ [0, 1000]:用std::vector或 C 风格数组,memo[n]是 O(1) 无哈希、无指针跳转,cache line 友好 - 参数是多个值(如
int i, int j, bool flag)或稀疏(n可能到 1e6 但只算 100 个点):必须用std::unordered_map,但 key 要手写operator==和std::hash - 别用
std::map:红黑树 O(log n) 查找,在记忆化场景下纯属降速,除非你真需要有序遍历
手写哈希 key 容易踩的坑
std::unordered_map 默认不支持结构体做 key,强行用会编译失败;自己写 hash 时常见错误:
- 用
^异或拼接多个字段:如a ^ b ^ c,顺序无关且a==b时抵消,碰撞率高;改用位移+异或:(a * 0x9e3779b9) ^ (b - 用
std::hash<int>{}(a)</int>直接哈希,但没处理struct { int x, y; }这种复合类型——必须显式特化std::hash或用std::tie(x, y)构造 tuple 后哈希(C++17 起) - key 中含指针或地址(如
this):不同运行实例地址不同,无法复用缓存;应转为逻辑标识(如 ID、name 字符串)
线程安全不是默认选项
静态局部 unordered_map 或全局缓存,在多线程递归中会崩:
-
memo[n] = fib(n-1) + fib(n-2);这行看似简单,但operator[]在 key 不存在时会插入默认值,再赋值——不是原子操作 - 两个线程同时发现
memo[42]不存在,都会执行计算并写入,结果未定义(可能覆盖、可能 double 计算) - 真要并发安全:用
std::shared_mutex+find()+try_emplace(),或更轻量的std::call_once包裹首次计算逻辑
缓存真正的门槛不在语法,而在对调用模式和数据分布的理解;一个没被触发的 cache hit,比没写的缓存还危险——它让你误以为优化了,其实只是掩盖了问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











