关于 accelint-ts-performance
用于系统审查和优化 JavaScript/TypeScript 性能的技能,指导分析瓶颈与反模式,并提供优化方法和正确性检查流程。
主要功能
- 01系统化性能审查
- 02识别算法效率问题
- 03评估 CPU 与内存取舍
- 04按问题类别应用优化模式
- 05变更后验证正确性
适用场景
- 01审查 JavaScript 代码库性能
- 02查找低效循环和查找操作
- 03优化内存或 I/O 行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gohypergiant/ agent-skills --skill accelint-ts-performance安装后在 Claude Code 中输入/accelint-ts-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
AGENTS.mdREADME.mdassets/output-report-template.mdreferences/avoid-allocations.mdbatching.mdbounded-iteration.mdcache-property-access.mdcache-storage-api.mdcurrying.mddefer-await.mdmemoization.mdobject-operations.mdperformance-misc.mdpredictable-execution.mdquick-reference.mdreduce-branching.mdreduce-looping.md详细介绍
TypeScript 性能优化
针对 JavaScript/TypeScript 代码库的系统性性能优化。结合审计工作流与专家级优化模式,提升运行时性能。
性能优化时绝对不要做的事
注意: 对于通用最佳实践(例如使用 any/enum 保证类型安全、避免使用 null、不要修改参数),请改用 accelint-ts-best-practices 技能。本节仅关注性能相关的反模式。
-
绝不要假定代码属于冷路径——工具函数、格式化器、解析器和验证器看起来简单,但经常会在循环、渲染管线或实时系统中被调用。务必审计所有代码,检查是否存在性能反模式。不要臆测代码的使用频率,也不要因其看似简单而跳过审计。
-
绝不要盲目应用所有优化——性能模式各有取舍。应在优化收益与代码复杂度之间取得平衡。开展审计时,应通过系统性分析识别所有反模式,并报告预期收益。由用户根据具体情况决定采用哪些优化。
-
绝不要忽视算法复杂度——对 O(n²) 代码进行微优化无济于事。对于 n=1000,算法层面的修复(O(n² → O(n))可带来 1000 倍加速;微优化最多只能达到 1.1–2 倍。先修复算法:使用 Maps/Sets 实现 O(1) 查找、消除嵌套迭代,并选择合适的数据结构。
-
绝不要为了速度牺牲正确性——性能问题依然是问题。优化经常会破坏边界情况:手动循环中的差一错误、空数组行为错误、
null处理问题。请验证优化前后的行为一致。优化前应添加涵盖边界情况的全面测试——在生产环境中发现 Bug 的代价远高于任何性能收益。 -
绝不要优化你不负责的代码——共享工具、库的内部实现,或他人正在积极开发的代码,可能引发合并冲突、重复劳动和归属混乱。性能改动会影响所有调用方;应与负责人协调,或等代码稳定后再进行优化。
-
绝不要忽视内存与 CPU 之间的权衡——缓存以空间换速度。在长时间运行的应用中,无界记忆化会导致内存泄漏。如果 CPU 提速 2 倍却让内存使用量增加 10 倍,可能导致 OOM 崩溃或频繁的 GC 暂停(比原本的慢更糟)。应同时分析内存和 CPU 使用情况;限制缓存大小;对于生命周期受限的缓存,使用 WeakMap。
-
绝不要假定优化在所有环境中都有效——V8 优化在不同 Node.js 版本(v18 与 v20)、浏览器(Chrome 与 Safari)和架构(x64 与 ARM)之间有所不同。在 Chrome 中可提速 3 倍的优化,在 Safari 中可能导致性能下降 1.5 倍。发布前应在所有目标环境中进行性能分析;针对特定环境的优化应保留回退实现。
-
绝不要链式调用数组方法(.filter().map().reduce())——每个方法都会创建中间数组并单独遍历。对于包含 10k 个元素的数组,
.filter().map()会分配 10k + 5k 个元素(若 50% 通过筛选),并遍历两次。使用单次reduce遍历,一次完成操作且不产生中间分配,可在热点路径中提速 2–5 倍。 -
绝不要在重复查找时使用
Array.includes()——Array.includes() 是 O(n) 线性搜索。对数组中的 100 个元素逐一检查 1000 个项目,复杂度为 O(n×m) = 100k 次操作。改用Set.has():通过哈希表实现 O(1) 查找,可将 100k 次操作降至 1000 次,提速约 100 倍。提前构建一次 Set;其摊销成本可以忽略。 -
永远不要在确认是否需要结果之前就使用
await——await会立即暂停执行,即使你并不需要该值。将await移到实际需要结果的条件分支中。例如:const data = await fetch(url); if (condition) { use(data); }在condition为 false 时会浪费 I/O 时间。更好的做法是:if (condition) { const data = await fetch(url); use(data); },这样在不需要时就会跳过 fetch。 -
永远不要在循环中重新计算常量——每次迭代都重新计算不变量会浪费 CPU。循环 10k 次时,
array.length查询(即使引擎会缓存)或Math.max(a, b)都会无谓地运行 10k 次。将不变量提到循环外:const len = array.length; for (let i = 0; i < len; i++),或使用柯里化函数,只预先计算一次常量参数。 -
永远不要创建无界循环或队列——这可以防止错误或恶意输入导致资源消耗失控。设置明确的限制(
for (let i = 0; i < Math.min(items.length, 10000); i++))或超时。无界循环可能会冻结 UI 线程;无界队列会导致 OOM 崩溃。应设置清晰的限制并快速失败,而不是逐渐退化到无法使用。 -
永远不要在热点路径中使用
try/catch——V8 无法内联包含 try-catch 块的函数,并会将整个函数标记为不可优化。在热点循环中使用单个 try-catch,就会因阻止内联、逃逸分析和其他优化而导致性能降低 3-5 倍。应在进入热点路径前使用类型守卫验证输入;将 try-catch 移到循环外以包裹整个操作;对预期错误使用 Result 类型。
优化性能前,请先思考
应用以下检查,以有效地集中优化工作:
影响评估
- 这段代码真的慢吗? 有性能分析数据时,应据此确定优先级。没有数据时,应审查所有代码,找出反模式。
- 它占运行时间的百分之几? 有性能分析数据时,火焰图有助于找出影响最大的问题。没有数据时,应报告发现的所有反模式。
- 原始性能很重要——无论当前使用场景如何,都应审查所有代码中的性能反模式。即使工具函数、格式化器、解析器和数据转换看起来很简单,也经常会在循环、渲染流水线或实时系统中被调用。
正确性验证
- 我有覆盖这段代码的测试吗? 性能问题往往很隐蔽。全面的测试可以发现优化导致的回归。优化前应添加测试。
- 有哪些边界情况? 手动优化循环时,更容易出现差一错误、空数组以及 null/undefined 值等问题。应进行全面测试。
复杂度与收益
- 算法复杂度是否已达到最优? O(n) → O(1) 可带来 1000 倍的加速。微优化最多只能带来 1.1-2 倍的提升。应先改进算法。
- 这项优化会长期有效吗? 如果代码经常变更,优化可能很快就会被舍弃。应先优化稳定的代码。
- 可读性会受到多大影响? 手动循环比
.map()快,但也更难维护。应在性能与团队效率之间取得平衡。
使用方法
此技能采用渐进式披露,以尽量减少上下文占用:
1. 从工作流开始(SKILL.md)
按照下方的 4 阶段审查工作流,系统地分析性能。
2. 参考性能规则概览(AGENTS.md)
加载 AGENTS.md,浏览按类别整理的精简规则摘要。
3. 按需加载具体的性能模式
发现具体的性能问题时,加载相应的参考文件,查看详细的 ❌/✅ 示例。
4. 使用报告模板(针对明确的审查请求)
用户明确要求进行性能审查时,加载模板,以便生成格式一致的报告:
- assets/output-report-template.md - 提供指导的结构化模板
性能优化工作流
两种运行模式:
-
审计模式——直接调用技能(
/accelint-ts-performance <path>)或用户明确请求性能审计- 使用模板生成结构化审计报告(仅限阶段 1–2)
- 在实施前报告发现的问题,供用户审阅
- 由用户决定应用哪些优化
-
实施模式——在功能开发过程中自动触发技能
- 直接识别并应用优化(全部 4 个阶段)
- 无需正式报告
- 专注于就地修复问题
复制此清单以跟踪进度:
- [ ] Phase 1: Profile - Identify actual bottlenecks using profiling tools
- [ ] Phase 2: Analyze - Categorize issues by impact and optimization category
- [ ] Phase 3: Optimize - Apply performance patterns from references/
- [ ] Phase 4: Verify - Measure improvements and validate correctness
阶段 1:分析性能,识别瓶颈
重要:审查所有代码中的性能反模式。 不要根据对使用频率的假设跳过代码。即使工具函数、格式化器、解析器、验证器和数据转换的实现看起来很简单,它们也经常会在循环、渲染管线或实时系统中被调用。
如果有可用的性能分析工具,请使用它们建立基准测量:
- 浏览器:Chrome DevTools 的 Performance 标签页
- Node.js:
node --prof script.js && node --prof-process isolate-*.log
无论是否有性能分析数据:都要对代码进行系统性静态分析,以识别所有性能反模式:
- O(n²) 复杂度(嵌套循环、重复搜索)
- 过度分配(模板字面量、对象展开、数组方法)
- 在使用 String() 即可满足需求时分配模板字面量
- 链式调用数组方法(.filter().map())
- 阻塞异步操作
- 循环中的 try/catch
输出:列出所有已识别反模式及其位置和预期性能影响。不要按“严重程度”或“优先级”筛选——报告发现的所有问题。
生成审计报告时(直接通过 /accelint-ts-performance <path> 调用技能,或用户明确请求性能审计时),请使用结构化模板:
- 加载 assets/output-report-template.md,了解报告结构
- 按照模板的说明统一格式并对问题分组
阶段 2:分析并分类问题
对阶段 1 中识别的每个问题,按优化类型进行分类:
按优化类型对所有问题进行分类:
| 问题类型 | 类别 | 预期提升 |
|---|---|---|
| 嵌套循环、O(n²) 复杂度 | 算法优化 | 10-1000x |
| 重复执行高开销计算 | 缓存与记忆化 | 2-100x |
| 分配密集型代码 | 减少分配 | 1.5-5x |
| 违反顺序访问原则 | 内存局部性 | 1.5-3x |
| 过多 I/O 操作 | I/O 优化 | 5-50x |
| 阻塞异步操作 | I/O 优化 | 2-10x |
| 循环中的属性访问 | 缓存与记忆化 | 1.2-2x |
问题映射速查:
加载 references/quick-reference.md,查看详细的问题到类别映射和反模式检测说明。
输出: 列出所有问题及其优化类别。不要筛选或排序优先级——列出阶段 1 中发现的所有问题。
阶段 3:使用性能模式进行优化
步骤 1: 根据阶段 2 的分析,确定瓶颈类别。
步骤 2: 加载对应类别的必读参考资料。完整阅读每个文件,不限制范围。
| 类别 | 必读文件 | 可选 | 不要加载 |
|---|---|---|---|
| 算法(O(n²)、嵌套循环、重复查找) | reduce-looping.md reduce-branching.md | — | memoization、caching、I/O、allocation |
| 缓存(循环中的属性访问、重复计算) | memoization.md cache-property-access.md | cache-storage-api.md(适用于 Storage API) | I/O、allocation |
| I/O(阻塞异步操作、过多 I/O 操作) | batching.md defer-await.md | — | algorithmic、memory |
| 内存(分配密集、GC 压力) | object-operations.md avoid-allocations.md | — | I/O、caching |
| 局部性(违反顺序访问、缓存未命中) | predictable-execution.md | — | 所有其他文件 |
| 安全性(无界循环、队列失控) | bounded-iteration.md | — | 所有其他文件 |
| 微优化(热路径微调、1.1-2 倍提升) | currying.md performance-misc.md | — | 所有其他文件(仅在完成算法优化后应用) |
注意:
- 如果瓶颈涉及多个类别,请加载所有相关类别的参考资料
- 仅在以下条件都满足时应用微优化:瓶颈位于热路径中、已完成算法优化、还需要额外的 1.1-2 倍性能提升
步骤 3:优化期间查阅快速参考
加载 AGENTS.md,查看按类别整理的精简规则摘要。在根据上述详细参考文件应用模式时,可将其用作快速查阅资料。
系统地应用模式:
- 加载参考文件,了解已识别问题所属类别
- 浏览 ❌/✅ 示例,寻找匹配的模式
- 应用优化,尽量减少改动以保证正确性
- 添加注释,说明优化内容并引用对应模式
优化示例:
// ❌ Before: O(n²) - nested iteration
for (const user of users) {
const items = allItems.filter(item => item.userId === user.id);
process(items);
}
// ✅ After: O(n) - single pass with Map lookup
// Performance: reduce-looping.md - build lookup once pattern
const itemsByUser = new Map<string, Item[]>();
for (const item of allItems) {
if (!itemsByUser.has(item.userId)) {
itemsByUser.set(item.userId, []);
}
itemsByUser.get(item.userId)!.push(item);
}
for (const user of users) {
const items = itemsByUser.get(user.id) ?? [];
process(items);
}
阶段 4:验证改进效果
衡量性能提升:
- 使用相同输入重新运行性能分析器
- 比较优化前后的运行时间百分比
- 记录加速倍数(例如:“快 2.3 倍”)
验证正确性:
- 运行现有测试套件——所有测试都必须通过
- 针对受优化影响的边界情况添加新测试
- 对面向用户的功能进行手动测试
记录优化:
// Performance optimization applied: 2026-01-28
// Issue: Nested iteration causing O(n²) complexity with 10k items
// Pattern: reduce-looping.md - Map-based lookup
// Speedup: 145x faster (5200ms → 36ms)
// Verified: All tests pass, manual QA complete
决定是否保留优化:
- 提速 >10 倍: 只要测试通过,就始终保留
- 提速 2-10 倍: 如果测试通过且代码仍易于维护,则保留
- 提速 1.2-2 倍: 对热路径(每秒执行次数 >1000)或实时系统予以保留
- 提速 1.05-1.2 倍: 仅当改动微不足道,或涉及关键渲染/动画循环时保留
- 提速 <1.05 倍: 回退,除非可读性也有所提升
实时系统(60fps 渲染、实时数据可视化): 即使是 1.05 倍的提升,在关键热路径中也有意义。使用帧时间分析器验证其对帧预算的影响(60fps 下为 16.67ms)。
如果测试失败: 修复优化或回退。性能问题也是问题。
自由度校准
根据优化带来的影响校准指导的具体程度:
| 优化类型 | 自由度 | 指导形式 | 示例 |
|---|---|---|---|
| 算法(提升 10 倍以上) | 中等自由度 | 提供多种有效方案,根据约束条件选择 | “使用 Map 实现 O(1) 查找,或使用 Set 去重” |
| 缓存(提升 2–10 倍) | 中等自由度 | 提供带示例的模式和缓存失效策略 | “如果生命周期与源对象一致,则使用 WeakMap 进行记忆化” |
| 微优化(提升 1.1–2 倍) | 低自由度 | 使用参考资料中的确切模式,先进行测量 | “在循环中缓存 array.length:for (let i = 0, len = arr.length; ...)” |
判断标准:“能提速多少,维护成本又是多少?”
- 提速 10 倍以上 → 值得增加复杂度,采用中等自由度并提供模式
- 提速 2–10 倍 → 用测量结果证明其合理性,采用中等自由度
- 提速 1.2–2 倍 → 对热点路径和实时系统很有价值,采用低自由度并使用确切模式
- 提速 1.05–1.2 倍 → 仅在改动极其简单或属于关键热点路径(60fps 渲染等)时采用
重要说明
- 全面审计理念——审计所有代码,查找性能反模式。即使实用函数、格式化器、解析器和验证器看起来很简单,它们也常常在循环或实时系统中被频繁调用。不要假设它们的使用频率。
- 报告所有发现——无论是否有性能分析数据,都要进行系统化静态分析,找出并报告所有反模式及其预期收益。不要根据“严重程度”或“优先级”进行筛选。
- 参考文件具有权威性——references/ 中的模式均已验证。除非测量结果证明另有更优方案,否则请严格遵循。
- 热点路径的定义——每次用户交互执行超过 1000 次,或在服务端场景中每秒执行超过 100 次的代码。对于实时系统(60fps 渲染、实时可视化),热点路径是指关键渲染循环中每帧耗时超过 1ms 的函数。
- 实时系统有更严格的要求——60fps 的帧预算为 16.67ms,120fps 的帧预算为 8.33ms。热点路径即使提升 1.05 倍也很有价值。应使用帧耗时进行性能分析,而不只是总执行时间。
- 回归测试——性能优化经常会在边缘情况下引入细微错误。优化前应添加测试。
- 内存性能分析很重要——某些优化(记忆化、缓存)会以占用内存换取速度。应在生产环境中监控内存使用量,尤其是长时间运行的实时应用。
快速决策树
使用此表快速确定适用的优化类别。
全面审查:无论代码当前的使用场景如何,都要识别其中的所有性能反模式。报告所有发现并说明预期收益。
| 如果你看到…… | 根本原因 | 优化类别 | 预期收益 |
|---|---|---|---|
对同一数据进行嵌套 for 循环 | O(n²) 复杂度 | 算法优化(减少循环) | 10-1000 倍 |
先调用 .filter(),再调用 .find() 或 .map() | 多次遍历数据 | 算法优化(减少循环) | 2-10 倍 |
重复调用 array.find() 或 .includes() | O(n) 线性搜索 | 算法优化(减少循环,使用 Set/Map) | 10-100 倍 |
对同一变量使用大量 if/else 分支 | 分支密集型代码 | 算法优化(减少分支) | 1.5-3 倍 |
| 多次使用相同参数调用同一函数 | 重复计算 | 缓存(记忆化) | 2-100 倍 |
在循环中多次访问 obj.prop.nested.deep | 属性访问开销 | 缓存(缓存属性访问) | 1.2-2 倍 |
在循环中调用 localStorage.getItem() 或访问 sessionStorage | 循环中的高开销 I/O | I/O(批处理,延迟 await) | 5-20 倍 |
按顺序多次调用 await fetch() | 顺序 I/O 阻塞 | I/O(批处理,延迟 await) | 2-10 倍 |
在可能不需要结果的条件判断之前使用 await | 过早挂起异步操作 | I/O(延迟 await) | 1.5-3 倍 |
大量使用对象展开 {...obj} 或数组展开 [...arr] | 分配开销 | 内存(避免分配) | 1.5-5 倍 |
| 在热点循环中创建对象或数组 | 分配导致 GC 压力 | 内存(避免分配) | 2-5 倍 |
在可以安全修改对象时使用 Object.assign() 或展开语法 | 不必要的不可变性开销 | 内存(对象操作) | 1.5-3 倍 |
| 非连续访问数组元素 | 缓存局部性问题 | 内存局部性(可预测执行) | 1.5-3 倍 |
使用 while(true) 或让队列无限增长 | 资源使用失控 | 安全性(限制迭代次数) | 防止崩溃 |
| 调用函数时前 N 个参数大多相同 | 重复传递参数 | 微优化(柯里化) | 1.1-1.5 倍 |
在热点循环中使用 try/catch | V8 反优化 | 微优化(其他性能优化) | 3-5 倍 |
在循环中使用 + 拼接字符串 | 字符串复制呈二次方增长 | 微优化(其他性能优化) | 2-10 倍 |
如何使用此表:
- 根据性能分析器发现的瓶颈识别模式
- 在“如果你看到……”列中查找对应行
- 跳转到第 3 阶段中对应的优化类别
- 加载该类别的必需参考文件