应先通过日志确认递归函数中相同参数是否被重复调用,再结合XHProf定位高频递归函数;静态缓存需注意参数可缓存性、多请求隔离及键生成可靠性,避免因对象引用、浮点精度等问题导致缓存失效或污染。

直接看缓存是否生效,而不是先猜哪段代码慢——重复计算点藏在“相同输入反复进递归体”里,不打日志根本发现不了。
用静态缓存 + 日志确认重复调用
PHP 递归函数里最隐蔽的重复计算,不是逻辑写错,而是同一组参数被反复传入。光靠 xdebug 堆栈看不出重复,必须主动记录输入组合。
- 在递归函数开头加:
static $cache = [];和var_dump([$arg1, $arg2]);(或更轻量的error_log("call: " . json_encode([$arg1, $arg2]));) - 运行后检查日志:如果同一组
[$id, $mode]出现 3 次以上,就是典型重复点 - 注意:含
time()、rand()或全局变量的参数,缓存会失效,日志里会出现看似不同实则语义相同的输入
用 XHProf 定位高频递归函数
XHProf 不会告诉你“哪里重复”,但它能暴露“谁被调了太多次”。重点看两个指标:
-
Called列数值异常高(比如单次请求调用 5000+ 次),而Exclusive Time却不高 → 很可能是子问题爆炸式展开 - 函数名在调用树里反复出现,且父级几乎全是自己(如
parseNode→parseNode→parseNode)→ 说明没缓存,每次都在重算 - 避免用
xdebug在生产环境采样,开销太大;XHProf的采样模式更适合抓这类高频低耗时调用
检查参数是否真“可缓存”
不是所有递归都适合加缓存。加了反而出错的常见情况:
立即学习“PHP免费学习笔记(深入)”;
- 参数含对象引用(
$node是对象,但没实现__toString或serialize),导致json_encode或数组键生成失败 - 用了
static $cache但没考虑多线程/多请求隔离 —— PHP-FPM 下每个 worker 进程独立,一般没问题;但 CLI 模式下多次调用会累积缓存,可能污染后续执行 - 缓存键用了
md5(serialize($args)),但serialize对浮点数精度敏感,0.1 + 0.2和0.3可能生成不同键
真正难的是判断“该不该缓存”——不是所有重复都是浪费,有些是数据天然多叉,比如解析一棵带环引用的配置树,缓存键设计不对,反而让问题更隐蔽。



















