关于 codeprobe-performance
面向审查代码库的开发者,用于发现性能与扩展性问题的代码审计技能。检查查询模式、内存使用、缓存、并发、算法效率和前端性能。
主要功能
- 01检测 N+1 与无界查询
- 02检查索引和缓存行为
- 03标记内存与算法效率问题
- 04审查并发风险
- 05发现前端性能问题
适用场景
- 01审计请求路径的查询开销
- 02检查大型数据集处理方式
- 03检查缓存失效与任务幂等性
- 04定位高开销的前端渲染或导入
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ machineroom-dev/ codeprobe --skill codeprobe-performance安装后在 Claude Code 中输入/codeprobe-performance确认已加载。
详细介绍
独立模式
如果直接调用(而非通过编排器调用),必须先:
- 阅读
../codeprobe/shared-preamble.md(相对于此 SKILL.md 的位置解析——即同级的codeprobe技能目录,而非用户的项目),了解输出约定、执行模式和限制。 - 根据项目的技术栈,加载
../codeprobe/references/中适用的参考文件(按相同方式解析路径)。 - 除非用户另有指定,否则默认使用
full模式。
性能与可扩展性审计器
范围
此子技能会检测以下类别的性能与可扩展性问题:
- N+1 查询 — 在循环中延迟加载关联
- 缺少索引 — 在未建立索引的列上执行 WHERE/ORDER BY
- 无界查询 — 未分页或限制结果的 Model::all()
- 内存 — 将整个文件加载到内存中、在循环中累积数组
- 缓存 — 重复执行相同查询、缺少 TTL、写入后缓存过期
- 算法效率 — 热路径中的 O(n^2)、嵌套循环、在循环中排序
- 并发 — 竞态条件、非幂等队列任务、共享可变状态
- 前端性能 — 不必要的重新渲染、包体积、缺少延迟加载
不会标记的问题
- 非热路径代码中的过早优化 — 应按比例进行设计。启动时只调用一次的工具函数,不需要像请求处理程序一样优化。
- 仅用于开发的调试查询 — 在数据填充器、仅供开发使用的命令或调试端点中的查询。
- 批处理脚本 — 有意处理所有数据的脚本(迁移、数据回填),此类脚本使用无界查询可能是合理的。
- 小型且有界的集合(<100 项)上的 O(n^2) — 对已知小型数组执行嵌套循环没有问题。
- 前端 SSR/构建时代码 — 服务端组件和构建脚本的性能特征与客户端代码不同。
检测说明
N+1 查询
| ID 前缀 | 检测内容 | 检测方法 | 严重性 |
|---|---|---|---|
PERF | 在循环中访问 Eloquent 关联,但未进行预加载 | 搜索遍历集合的 foreach/for 循环,然后检查循环体内是否访问关联属性(例如 $order->items、$user->profile)。检查生成该集合的查询是否使用了 with() 或 load() 加载该关联。 | Major |
PERF | 迭代期间发生任何 ORM 延迟加载 | 查找在循环中隐式触发数据库查询的模式:Django QuerySet 在每次迭代时访问、SQLAlchemy 延迟加载、在 .map() 中访问 Prisma 关联。 | Major |
PERF | 模板/视图触发查询 | Blade 模板、Jinja2 模板或 React 组件在渲染期间调用会触发查询的关联属性。 | Major |
缺少索引
| ID 前缀 | 检测内容 | 检测方法 | 严重性 |
|---|---|---|---|
PERF | 在未建立索引的列上执行 WHERE/ORDER BY | 对照迁移文件或架构定义,检查查询条件(where()、orderBy()、WHERE、ORDER BY)是否有匹配的索引。 | Major |
PERF | 外键未建立索引 | 检查迁移文件中的 foreignId()、foreign()、references() 是否有对应的索引定义。大多数 ORM 会自动添加这些索引,但原始迁移可能会遗漏。 | Minor |
PERF | 需要复合索引的组合查询 | 同一查询中对不同列使用多个 where() 条件,或使用 where() + orderBy() 组合;这类查询可从复合索引中受益。 | Minor |
无界查询
| ID 前缀 | 检测内容 | 检测方法 | 严重程度 |
|---|---|---|---|
PERF | Model::all() 或不带限制的 SELECT * | 搜索 .all()、::all()、findAll()、不带 LIMIT 的 SELECT * FROM、paginate()、take() 或 limit()。 | 重大 |
PERF | 处理大型数据集时缺少游标/分块 | 处理大型数据集时,调用 get() 或加载整个集合的操作。应使用 cursor()、chunk()、lazy() 或等效的流式处理方式。 | 重大 |
内存
| ID 前缀 | 检测内容 | 检测方法 | 严重程度 |
|---|---|---|---|
PERF | 将整个文件加载到内存中 | 对用户上传的文件或大型文件调用 file_get_contents(),对大小可变的文件调用 fs.readFileSync(),以及不限制读取大小的 Python open().read()。 | 重大 |
PERF | 在循环中累积数组 | 在循环中不断增长且没有上限或清理机制的数组——在内存中收集本可通过流式处理或生成方式处理的结果。 | 次要 |
PERF | 使用大型集合而非生成器 | 对于大型数据集,返回完整数组/列表,而使用生成器(yield)、惰性集合或迭代器会更节省内存。 | 次要 |
缓存
| ID 前缀 | 检测内容 | 检测方法 | 严重程度 |
|---|---|---|---|
PERF | 同一请求中重复执行相同查询 | 在单个请求/函数调用中多次执行相同的查询模式,且没有缓存结果。 | 次要 |
PERF | 缓存未设置 TTL | Cache::put()、cache.set()、没有过期时间的 Redis SET。永久缓存的数据可能会过时。 | 次要 |
PERF | 写入后未使缓存失效 | 创建/更新/删除操作未清除或更新相关缓存条目。数据变更后仍提供过时的缓存。 | 重大 |
算法效率
| ID 前缀 | 检测内容 | 检测方法 | 严重程度 |
|---|---|---|---|
PERF | 热点路径中的 O(n^2) 或更差复杂度 | 嵌套循环遍历相同或相关的集合。在循环中使用 array_search/in_array/includes()(在循环中线性搜索即为 O(n^2))。 | 重大 |
PERF | 在循环中排序 | 在循环体内调用 sort()、usort()、array_sort()、.sort()。 | 重大 |
PERF | 可用哈希映射替代的线性搜索 | 在同一数组上重复使用 in_array()、.includes()、.indexOf()、list.index(),而预先构建 Set/字典/哈希映射后,每次查找即可达到 O(1)。 | 次要 |
并发
| ID 前缀 | 检测内容 | 检测方法 | 严重程度 |
|---|---|---|---|
PERF | 读-改-写中的竞态条件 | 在没有锁的情况下读取值、修改值并写回的模式:在并发环境中递增计数器、更新余额、切换标志。 | 严重 |
PERF | 队列任务缺少幂等性 | 未检查重复执行的队列/任务处理程序。创建资源时未检查资源是否已存在的任务。任务创建的数据缺少唯一约束。 | 重大 |
PERF | 异步上下文中的共享可变状态 | 异步处理程序访问全局/模块级可变变量,或将请求范围的数据存储在模块作用域中。 | 重大 |
前端性能
| ID 前缀 | 检测内容 | 检测方式 | 严重程度 |
|---|---|---|---|
PERF | 不必要的重复渲染 | 对昂贵的计算,或每次渲染都会收到新对象/数组引用的组件,缺少 React.memo、useMemo、useCallback。在 JSX 属性中内联创建对象/数组。 | 轻微 |
PERF | 导入了体积较大的依赖包 | import _ from 'lodash'(导入整个库)、import moment from 'moment'(体积较大的库,而 date-fns 或 dayjs 已足够)、import * as icons from 'icon-library'。 | 轻微 |
PERF | 缺少延迟加载 | 没有使用 React.lazy() / 动态 import() 按路由拆分代码。较重的组件在初始页面加载时就被 eager 加载。 | 轻微 |
可选脚本集成
当 scripts/complexity_scorer.py 的输出可用时(由编排器在 /codeprobe audit 期间运行),使用它来识别复杂度较高的函数,并将其作为潜在性能热点候选。评级为 "high" 或 "very_high",且出现在热点路径(请求处理程序、循环体、频繁调用的实用工具函数)中的函数,是发现算法效率问题的强烈信号。
ID 前缀和修复提示词示例
所有发现均使用 PERF- 前缀,并按顺序编号:PERF-001、PERF-002 等。
修复提示词示例
- "在
OrderController@index(第 22 行)中,为Order::query()调用添加->with('items', 'customer'),以修复 N+1 问题——当前在orders/index.blade.php:15的 Blade 循环中延迟加载 2 个关联。" - "将
CatalogService.php第 30 行的Product::all()替换为Product::query()->paginate(25),或者在处理所有记录时使用Product::cursor()。当前查询会将所有产品加载到内存中。" - "在
ReportGenerator@aggregate(第 45–60 行)中,遍历$customers内部的$orders的嵌套循环复杂度为 O(n*m)。在外层循环之前构建查找哈希映射:$ordersByCustomerId = collect($orders)->groupBy('customer_id')。" - "将
src/utils/helpers.ts第 3 行的import _ from 'lodash'替换为特定导入:import debounce from 'lodash/debounce',以减小打包体积。"
评论
支持 Markdown