
1. 「感觉慢」和「hotspot 在 decode」差一个 profiler
Release 节点日志看不出卡——perf 才发现百分之十二在 Eigen gemm、百分之九在 malloc,后者是每帧 vector reserve 没复用 buffer。机器人 pipeline 要先 wall clock 分段,再对 top function 做 line-level,别一上来改算法。Profiling 是放大镜——先对准再动刀;优化错层(IO wait 当算法慢)是常见 self-deception,改完 SIMD 端到端 latency 不变就是信号。感觉慢和 hotspot 在 decode 之间,差的就是一次 thirty 秒 perf record。
2. perf 与火焰图
perf record -g -F 997 -p $(pidof slam_node) -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > slam.svg
perf report --no-children看 inclusive 百分比与 call chain——Eigen 内联可能显示为一大块 operator*。找「宽 plateau」——优化那里才划算。colcon Release 默认 strip,field 用 RelWithDebInfo 保留符号;annotate 难读时在热点外包裹 C 锚点函数当栈标记。Firefox Profiler 导入 perf script 比文本直方图直观。attach 前确认 pid 与 build 类型,strip 后的 field 包要先换带符号构建再采。perf 采样窗口应覆盖负载高峰,别在 idle 时段采三十秒然后声称「无热点」。
3. 微基准 vs 系统
Google Benchmark 测函数;perf 测整节点含锁 wait。两者结合:benchmark 验证 micro 优化,perf 验证没增加别处 wait。改代码前后固定同一 bag slice replay,比 micro-bench 更接近集成瓶颈——后者容易优化了 idle 时段。Throttled log 打 steady_clock 段耗时,和 perf 栈顶函数名对照,避免优化错层。Tracy zone 名 LidarDecode/ScanMatch 足够;每点 InsertPoint 会拖垮 profiler。wall clock 分段与 perf 应对同一 bag 窗口,否则 log 说 decode 慢、火焰图却宽在 malloc,是采样窗口不一致而非矛盾。
4. off-CPU 与锁等待
mutex 争用大时 CPU flame 扁——perf sched 或 eBPF off-wake 看谁在等。pthread_cond_wait 占百分之三十以上,优先查 executor/DDS 而非继续 SIMD 点云循环。盲目 -O3 vectorize 解不了锁等待,cache miss 高时亦然。perf stat cache-misses 附在 flame 结论旁。hot function 指令数很少但 miss 极高,优先改数据布局而非 -O3。Jetson 用 Nsight Systems 对 CPU 加 CUDA 同轴,别把 GPU idle 当成 CPU 优化成功。off-CPU 分析与 on-CPU 火焰图应成对出现在性能 PR 里,否则容易只优化算力、忽略等锁。
5. 机器人常见 hotspot
点云 memcpy/decode;TF lookup 每点一次(应 batch);日志 INFO 每帧。Hotspot 从百分之四十降到百分之八后,下一宽 plateau 才是目标——别只贴一张 before 图。确认 CPU 真满,别 IO wait mistaken 为算法慢。优化后 perf 不变,先查是否测了 idle 时段或 pid 不对。对 perf report 里 pthread_cond_wait 宽条,先查 executor 与 DDS 再动算法。Jetson 与 x86 lab 热点分布不同——在 Orin 上 profile 再决定绑核与 GPU 预处理,别照搬 lab 结论上 field。
6. 分段与集成验收
改代码前后固定同一 bag slice replay,对比 perf stat cycles 与端到端 latency p99。段耗时 log 与 perf 栈顶函数名一致,否则说明分段粒度或符号对不上。优化后重采 flame,确认热点转移而非 self-deception。同一 bag、同一 build、同一负载下对比 before/after——迭代采 profile,宽 plateau 优化后下一热点成为新目标。性能优化若无 p99 数据,默认视为未验证——均值 alone 在机器人栈里常误导。
7. 案例:malloc 热点来自未复用 buffer
某激光节点「感觉卡」,火焰图里 Eigen 不算宽,malloc 占百分之九——每帧 vector reserve 未复用,改成员 buffer 复用后 p99 降十五毫秒,SIMD 点云循环完全未动。根因不是算力,是分配器。教训:先看 inclusive 栈顶是算还是分配,再决定 vectorize 还是池化。性能 PR 应附 before/after 火焰图与同一 bag 的 p99,避免「优化了 micro、集成不变」合入仓库。
8. 验收
- 优化后重采 flame,热点转移可解释。
- off-CPU 与 on-CPU 分开看。
- 同一 bag slice、RelWithDebInfo 符号。
- 段耗时 log 与栈顶函数名一致。
- 宽 plateau 优化后列出下一热点目标。
先 end-to-end 再 micro-opt;profiling 对准了再动刀,比感觉慢就 vectorize 省一整周。集成瓶颈常在 IO、分配、锁等待,不在 inner loop 的最后一次 SIMD;性能 PR 应附同一 bag 的 p99 与火焰图前后对比;无 p99 数据的优化默认视为未验证,均值 alone 在机器人栈里常误导。
相关
也可以看看
johan's blog