| name | faster-code |
| description | 当代码运行缓慢、超时、被终止、消耗大量 CPU、执行长时间批处理作业、进行参数扫描、训练模型、处理大型数据集、运行模拟,或目标规模运行时间未知时,使用本技能。它指导智能体逐步探测运行时间,在可行时进行在线 N-T 进度插桩,采用固定超时采样,执行 N-T 扩展估算,并在尝试目标规模前作出全规模运行的通过/不通过决策。如果进行了优化,它要求保留慢速版本的规范输出,证明语义逐字节等价,并在通过并行投入更多 CPU 前优先消除无效工作。本技能不封装性能分析工具,也不提供通用优化方案;它负责判断应全规模运行、停止,还是转入性能分析和原则性优化。 |
faster-code
使用本技能改进计算密集型代码的运行方式:先判断目标规模能否在运行时间预算内完成,再决定是全规模运行、停止还是优化。不要一开始就让缓慢程序在长超时下运行。长超时只能证明浪费已经发生;短时有界探测可以更早提供决策信息。
核心目标
将“它能否在全规模下完成?”转化为运行前门禁:
- 定义目标规模
N_full 和可接受的运行时间预算 T_budget。
- 当程序具有自然工作计数器且源代码修改成本较低时,优先采用在线
N-T 进度插桩。
- 使用短时超时、进度日志或输入切片构建 3–5 个有效
N-T 样本。
- 使用多个简单模型估算目标规模运行时间。
- 如果估算结果不可接受,不要全规模运行;转入性能分析、诊断或优化。
如果代码经过优化,应增加语义门禁:保留慢速版本的规范输出作为真值,然后要求快速版本在同一输入切片上生成逐字节相同的规范输出。如果浮点数差异是自然现象,应在写入规范输出前,按照预先声明的舍入或格式规则对数值进行规范化。为便于审计,可以牺牲有限精度,但最终比较仍必须逐字节进行。
优化有明确的优先顺序:先避免无效计算,再投入更多计算资源。减少不必要的工作通常优于用更多核心掩盖浪费。并行可能有用,但应作为靠后选项,仅在算法或工作负载削减不可用、风险过高或不足以满足运行时间预算时采用。
在线 N-T 优先
在选择切片批量运行前,先检查程序是否有自然进度计数器。许多慢速程序已经包含遍历行、文件、任务、参数组合、批次、epoch、窗口或请求的循环。如果存在这样的计数器,且编辑代码风险较低,应先加入在线 N-T 进度日志,再执行一次有界探测。
缺少进度日志应视为可修复的插桩缺口,而不是程序只能批量运行的证据。
以下条件全部满足时,加入在线采样:
- 存在单调递增的已处理工作计数器,或可以低成本计算;
- 该计数器能映射到选定的
N,或映射到有充分依据的组合 N;
- 可以每 5–10 秒或每个较粗粒度的工作间隔输出日志,且不会产生显著开销;
- 当前任务允许修改源代码,且修改不会改变计算语义;
- 程序可在超时终止前刷新日志。
以下任一条件满足时,不要加入在线采样:
- 唯一可用的计数器需要高成本同步、全局扫描或大幅增加内存;
- 日志会显著扰动被测工作负载;
- 当前任务无法安全编辑代码;
- 工作确实不可分割,也不会暴露有意义的局部进度。
插桩应保持最小化且便于解析。优先使用一种稳定的行格式:
PERF_PROGRESS processed=120000 total=1000000 elapsed_sec=31.2 rate=3846.1 unit=rows
对于嵌套工作,应明确记录组合 N,不要隐藏第二个规模变量:
PERF_PROGRESS processed=240000 total=2000000 elapsed_sec=31.2 unit=row_param_pairs rows=120000 params=2
如果在线插桩可行,应先实现插桩,再重复运行切片探测。如果不可行,应在报告的 instrumentation_decision 中说明原因。
程序分类
A. 进度可观察程序
如果程序能在运行时输出进度,或能以较低成本修改为输出进度,应优先执行一次短时超时探测,而不是多次切片运行。
有用的进度日志应稳定、低频且便于解析:
PERF_PROGRESS processed=120000 total=1000000 elapsed_sec=31.2 rate=3846.1
步骤:
- 使用短时超时运行,通常为 30 秒。
- 从日志中提取多个
elapsed_sec -> processed_N 数据点。
- 如果已捕获进度日志,即使运行因超时被终止,也应将其视为有用样本。
- 如果吞吐量随时间下降,应保守外推;不要从早期最快速率进行外推。
如果缺少进度日志,但满足在线 N-T 条件,应先加入日志,再执行重复切片运行。每 5–10 秒记录一次,而不是每处理一个元素就记录。
B. 仅批量程序
只有在线 N-T 插桩不可用、不安全或会扭曲测量结果时,才使用数据切片和重复的有界运行。
步骤:
- 找出用于控制输入规模的参数,例如
--limit、日期范围、文件数量、采样比例、批次数量或参数数量。
- 从较小的
N 开始,并按指数扩大,例如 1k、2k、4k、8k。
- 当一次运行超时时,在上一个已完成的
N 和失败的 N 之间进行二分查找。
- 使用二分查找构建可靠的
N-T 样本,而不是寻找精确的最大可行 N。
- 不要浪费运行次数去命中精确秒数;目标是获得扩展趋势,而不是精确基准测试。
定义 N
外推前,先定义主要规模变量 N。不要假设 N 始终表示行数。
常见定义包括:
- 输入行数;
- 已处理的文件或对象;
- 生成的事件或任务;
- 参数组合;
- 工作进程、分片或独立单元;
- 训练窗口或批次;
rows x parameter_combinations x files 等组合变量。
如果程序有多个主导规模变量,应使用最能解释工作量的组合变量。如果 N 无法代表全规模计算,应将结果标记为 INCONCLUSIVE。
采样要求
默认规则:
- 外推到全规模前,至少需要 3 个已完成样本点。
- 最好有 3–5 个已完成样本点。
- 除非样本不稳定,否则通常不需要超过 5 个点。
- 探测应从 30 秒超时开始。
- 除非用户明确接受其成本,否则单个探测节点应避免超过约 180 秒。
有用的时间区间:
- 30 秒节点:20–40 秒内完成的样本可接受。
- 60 秒节点:45–75 秒内完成的样本可接受。
- 120 秒节点:90–150 秒内完成的样本可接受。
- 180 秒节点:140–220 秒内完成的样本可接受。
超时样本表示 T(N) > timeout。拟合时不要把它当作 T(N) = timeout。
至少记录:
N:
runtime_sec:
status: completed | timeout | failed
timeout_sec:
command:
slice_method:
notes:
如果可以获取,还应记录峰值内存、CPU 利用率、输出大小和缓存状态。
外推方法
不要只使用一种估算。至少比较以下三种视角,并作出保守决策。
1. 线性外推
T_full = T_last * N_full / N_last
这是下界检查。如果连线性估算都超过预算,通常应停止。
2. 幂律拟合
T = a * N^p
log(T) = log(a) + p * log(N)
用它估算扩展指数 p。
默认解释:
p <= 1.2:近似线性。
1.2 < p <= 1.6:需要谨慎;目标估算必须显著低于预算。
1.6 < p <= 2.2:高风险;通常应先优化或缩小范围。
p > 2.2:除非 N_full 非常小,否则不要盲目全规模运行。
3. 局部斜率外推
只使用最大的两个或三个已完成 N 样本估算后期增长。这可发现小样本运行很快、但大样本性能下降的情况。
如果线性、幂律和局部斜率估算结果差异很大,应将结果标记为 INCONCLUSIVE 或 FAIL。不要使用最乐观的估算批准全规模运行。
通过/不通过决策
必须且只能返回以下一项:
PASS: 可以运行目标规模。
FAIL: 不要运行目标规模;应先诊断或优化。
INCONCLUSIVE: 证据不足或不稳定;暂时不要运行目标规模。
默认失败条件:
- 已完成样本点少于 3 个。
N 不能代表主导计算。
- 线性外推已经超过
T_budget。
- 保守估算超过
T_budget 的 1.5 倍以上。
- 扩展指数明显差于线性,且
N_full 远超采样范围。
- 样本非单调或不稳定,且无法解释。
- 探测表明内存、输出大小或 I/O 可能成为瓶颈。
默认通过条件:
- 样本稳定。
N 可信。
- 多种估算都低于
T_budget。
- 最大样本与
N_full 相距不远,或扩展趋势接近线性。
优化后的语义门禁
当任务从“能否完成?”转向“让它更快”时,应先保护慢速版本的语义。慢速版本虽然缓慢,但通常更清晰、更可信。没有语义检查的快速版本可能只是更快地产生了不同结果。
步骤:
- 修改代码前,选择一个能够完成且覆盖关键路径的小型或中型样本。
- 用慢速版本生成规范输出,并将其保存在快速版本无法覆盖的位置。
- 定义规范字段、排序顺序、浮点数格式、时间戳格式、空值表示和随机性控制。
- 如果存在浮点数,在写入规范输出前定义固定的舍入或格式规则,包括小数位数、科学记数法、NaN/Inf 表示和负零处理。
- 使用相同输入、相同参数和相同环境假设,让快速版本生成候选规范输出。
- 最终比较必须逐字节相同。
- 不要用运行时容差比较取代规范化。容差只能体现在写入前的舍入或格式规则中。
- 语义检查通过前,快速版本不得取代慢速版本,也不得用于支持全规模结论。
规范输出应小而稳定。只包含可证明语义等价的内容,例如:
- 最终指标和关键中间指标;
- 每个元素、任务或结果的核心输出;
- 排序后的 ID、时间戳、分数、状态、标签或决策;
- 必需的聚合校验和或汇总行。
不要使用巨大的临时文件作为规范输出。规范输出用于审计语义,而不是复制每项产物。
优化后的附加通过条件:
- 慢速版本的规范输出存在且已保留。
- 快速版本的候选规范输出存在。
- 逐字节比较通过。
- 运行时间采样表明,快速版本提高了目标规模的可行性,且没有新增无法解释的不稳定性。
优化后的附加失败条件:
- 不存在慢速版本的规范输出。
- 快速版本的规范输出未做到逐字节相同。
- 浮点数在写入规范输出前未经过规范化。
- 在看到差异后修改了舍入或格式规则。
- 只有聚合指标相近,但重要的逐元素输出发生偏移。
优化优先级
本技能不规定固定优化方案,因为工作负载各不相同。但它规定优化顺序:先节省算力,再缩短实际运行时间。
需要优化时,应优先选择减少总工作量的改动:
- 消除重复、冗余或未使用的计算;
- 避免不必要的扫描、连接、排序、转换、序列化、网络调用或磁盘 I/O;
- 只有在能减少净工作量,且不会造成数据陈旧或内存负担过重时,才缓存或预计算;
- 当采样、性能分析或代码检查表明存在可避免的增长时,改变数据结构或算法;
- 使用正确的过滤、提前退出、去重、批处理、剪枝或增量处理缩小工作负载。
只有在检查无效工作后,才使用并行。增加工作进程可以缩短耗时,但会增加 CPU 总用量、内存压力、I/O 争用和运营成本。以下情况适合采用并行:
- 剩余工作是必要且相互独立的;
- 算法或工作负载削减已用尽,或对当前任务风险过高;
- 瓶颈并非已经出现在内存、磁盘、网络、锁争用或速率限制上;
- 并行版本仍能通过语义等价验证和有界
N-T 采样。
不要把多进程、多线程、GPU、更大型机器或更多分片作为首选修复方案,除非证据表明工作已经是必要的、分区合理,且并非由浪费主导。
性能分析器指引
本技能不封装也不规定性能分析工具。但当门禁返回 FAIL 或 INCONCLUSIVE 时,特别是在复杂度高、吞吐量下降、怀疑存在内存瓶颈或样本不稳定的情况下,应告知用户使用适合项目和语言的现有性能分析器或热点审计工具。
性能分析器是下一阶段的输入,不能取代本门禁。性能分析应保持短时、受控且可复现:
- 分析能够稳定触发慢速路径的小型或中型样本。
- 设置明确超时,避免性能分析演变成另一次盲目的全规模运行。
- 捕获热点函数、热点行、调用次数、累计时间、分配压力或 I/O 等待。
- 如果没有规范输出,不要根据性能分析结果重写语义敏感代码。
- 优化后重新使用本技能:通过规范输出逐字节门禁,然后重复
N-T 采样和全规模通过/不通过决策。
报告格式
使用以下结构:
Performance Gate Result: PASS | FAIL | INCONCLUSIVE
Target:
- N_full:
- T_budget:
- command:
- N_definition:
Program Type:
- progress_observable | batch_only
- sampling_method:
- instrumentation_decision:
Samples:
| N | runtime_sec | status | timeout_sec | notes |
Scaling:
- linear_estimate:
- power_law_p:
- power_law_estimate:
- local_slope_estimate:
- conservative_estimate:
Canonical Check, if optimized code was created:
- slow_canonical:
- fast_candidate_canonical:
- compare_rule:
- compare_result:
- semantic_gate:
Decision:
- conclusion:
- reason:
- optimization_priority, if optimized:
- next_step:
非目标
本技能不会:
- 封装性能分析工具;
- 提供通用优化方案;
- 自动重写算法;
- 在检查无效工作前优先使用并行;
- 证明估算绝对精确;
- 在证据不足时鼓励全规模运行。
如果门禁失败,应建议进行性能分析、诊断、算法修改、进度日志记录、采用更好的切片策略或重新定义 N。不要开始长时间的目标规模运行。
操作规则
当用户要求全规模运行、检查某项工作是否会超时或让慢速代码更快时,应在长时间执行前使用本技能。将超时视为采样信号,而不是最终答案。目标是尽早获取决策信息,避免把算力浪费在本可预见的失败运行上。