python 中 `@lru_cache` 的底层 c 实现会额外占用 c 栈空间,导致即使 python 层递归深度相同,c 层栈也极易溢出;而纯 python 递归在 3.11+ 已通过内联调用优化避免了 c 栈消耗,因此更耐深递归。
在解决树形动态规划(如 CSES 1674:统计每个员工的下属人数)时,你可能会自然写出递归 DFS,并尝试用 @lru_cache 加速。但令人困惑的是:*完全相同的树结构、相同的递归逻辑、甚至相同的 `sys.setrecursionlimit(2 109)设置,加了@lru_cache就崩溃(0xC00000FD`),不加却能稳定运行百万级节点——这并非算法问题,而是 Python 运行时底层机制的差异所致。
? 根本原因:C 栈 vs Python 栈
-
functools.lru_cache 在 CPython 3.11 及更早版本中是 纯 C 实现(见 _functoolsmodule.c),每次被装饰函数调用时,都会触发一次 C 函数调用链(_lru_cache_wrapper → 原函数)。这意味着:
✅ 每次递归不仅压入 Python 栈帧,还压入一个 C 栈帧;
❌ C 栈默认大小极小(Windows/Linux 通常仅 1–8 MB),对应安全递归深度仅数千到数万;
⚠️ 即使你用 sys.setrecursionlimit(2e9) 抬高 Python 限制,CPython 3.11 仍会盲目尝试执行超深 C 递归,最终 C 栈溢出,进程被操作系统强制终止(退出码 0xC00000FD)。 -
而纯 Python 递归(无装饰器)在 Python 3.11+ 中已启用 Inlined Python Function Calls(PEP 659 优化):当 Python 函数直接调用另一个 Python 函数时,解释器可跳过 C 层调度,复用当前 C 栈帧,仅增长 Python 栈。因此:
- sys.setrecursionlimit() 对其生效;
- 实际 C 栈深度几乎恒定(≈1),真正受限的是 Python 栈(可通过 setrecursionlimit 扩展);
- 故 200,000 层递归轻松通过,甚至测试 20,000,000 层也不会崩溃(只会抛 RecursionError)。
? 验证:Python 3.12 的行为变化
Python 3.12 明确分离了限制机制(What’s New):
“The recursion limit now applies only to Python code. Built-in functions do not use the recursion limit, but are protected by a different mechanism…”
这意味着: