
FrontierRefactor:多尺度、性能导向的代码库重构能力评测
首个以性能为导向的代码库重构 Benchmark,覆盖从单个计算例程到整个仓库的四种代码规模。
重构是软件工程中的一项常见工作,指在不改变程序外部可观察行为的前提下调整其内部实现,通常服务于两个目的:提升代码的可读性与可维护性,或提升程序的运行性能。前者调整代码结构、甚至更换编程语言往往就能见效,效果好坏取决于人的主观判断,难以直接衡量;后者要求准确判断程序开销的来源,并在外部行为不变的前提下将其消除,改动的效果直接体现在服务的响应速度与资源占用上,也可以被直接测量。因此以性能为导向的代码库重构任务更具工程挑战与现实意义。
由此我们提出 FrontierRefactor,一个以性能为导向的代码库重构 Benchmark。它按被重构代码的规模把任务分为四个尺度,从单个计算例程到一整个仓库。
本文介绍 FrontierRefactor 的设计:任务如何组织,功能测试与性能测试分别如何进行,以及如何防止这两项测试被绕过。在此基础上,我们发布公开子集 FrontierRefactor Preview-30,包含 30 个任务,并给出在这批任务上的评测结果。

为什么要评测以性能为导向的代码库重构
代码的执行效率直接影响服务的响应延迟与运行成本,也决定一次构建、一轮测试所需的时间。资深工程师的相当一部分精力因此花在优化已有实现上:用 Profiling 定位热点,把随数据量平方增长的操作换成更省时的写法,减少内存分配与拷贝,将 I/O 批量化,或更换执行后端。这类优化的关键在于判断开销的实际来源:分清哪些开销源于算法本身、哪些源于具体的实现方式,并只消除后者。这一判断依赖对程序执行过程的理解,无法照搬现成的写法。
这类工作已经进入前沿实验室的视野。OpenAI 在发布 GPT-5.1-Codex-Max 时,把项目级重构列为需要长时间连续工作才能完成的复杂任务之一。但现有的 Benchmark 与真实的重构需求并不匹配:已有工作各自只覆盖一种代码规模,KernelBench 评测单个 GPU Kernel 的改写,PIE 评测竞赛程序的加速,SWE-Perf 与 GSO 评测在已有仓库中修改代码以提升性能;而工程实践中的重构需求跨越多个规模,小至单个函数的局部优化,大至仓库级的整体重写。现有工作均未覆盖如此完整的规模跨度,其中仓库级的整体重写尤其缺乏评测。
FrontierRefactor 因此按被重构代码的规模设置四个尺度,依次为 Kernel 级(单个计算例程)、模块级、包级与仓库级。要判定一次重构是否合格,有三个问题必须解决:
- 如何构造覆盖充分的功能测试?测试须覆盖被重构代码对外承诺的全部功能,任何一项遗漏都可能使行为不一致的实现被判为通过。
- 如何测出可信的加速?判断是否变快要靠测量运行耗时,而耗时受机器状态干扰,测出的差异可能只是环境波动,而非代码本身变快。
- 如何防止这两项测试被绕过?被测的模型有可能不去优化代码,而是设法让性能测试与速度测量失效。
下文先介绍任务的组织形式,再依次说明这三个问题的解决办法。
FrontierRefactor 的设计
四个重构尺度
被重构的代码规模不同,可行的优化方式也不同。我们据此把任务分为四个尺度:
- Kernel 级:被重构的代码是单个计算例程。所要完成的计算由题面给定,可改的只有数据布局与指令顺序。
- 模块级:被重构的代码是一个函数或模块。函数签名与可观察行为须保持不变,算法与数据结构可以另选。
- 包级:被重构的代码是一个包的全部公开接口。接口的调用方式与返回结果须保持不变,其内部实现可以整体重新设计。
- 仓库级:被重构的代码是一整个仓库。程序对外的命令行为须保持不变,内部的结构与实现语言均可重新选择。
如表 1 所示,代码规模变大之后,有效的优化方式由局部改动转向整体重写,可用的编程语言也相应放宽:局部改动要在原有代码上进行,自然只能沿用原有代码的语言;整体重写要把全部对外行为重新写一遍,采用哪种语言便不再受限。
| 尺度 | 代码规模 | 主要优化方式 | 语言 |
|---|---|---|---|
| Kernel 级 | 一个计算例程 | 指令调度、内存布局 | 限原语言 |
| 模块级 | 一个函数或模块 | 更优的算法与数据结构 | 限原语言 |
| 包级 | 一个包的公开接口 | 接口整体重新实现 | 原语言与跨语言均可 |
| 仓库级 | 一整个仓库 | 整体重写,或在原有代码上优化 | 原语言与跨语言均可 |

任务形式
每个任务以 Harbor 格式封装,向被测 Coding Agent 提供一份任务说明和一个初始工作区。工作区中只有一份可正确运行的代码,称为参考实现,记作 S。Agent 在此基础上改写,最终留在工作区中的版本即为它的提交,称为候选实现,记作 S′。功能测试与计时所用的输入均由评测程序保管,不出现在工作区中,Agent 无法针对特定输入做特殊处理。
收到候选实现后,评测程序按固定次序判定:先检验功能是否保持不变,再测量运行速度提升了多少;功能未通过的提交不进入计时。两项判定都只比对程序的外部表现,不检查代码本身。任务因此不存在需要匹配的参考答案,采用何种实现思路不影响评分。
功能测试:如何覆盖全部行为
功能测试的可信度取决于测试的覆盖是否完整。测试由出题侧的一个 Agent 分三步准备,每一步的产物均交由熟悉该领域的工程师审查。
- 列出功能清单。若被重构的代码自带一套完整的测试,我们直接采用;若没有,则由该 Agent 通读代码,将其对外承诺的功能逐项列为清单。清单的粒度随尺度而定:包级与仓库级把对外接口分解成若干项操作,Kernel 级与模块级则列出一组必须保持不变的性质,包括返回值、数据类型、元素顺序、异常行为,以及输入是否被原地修改。
- 按清单生成测试。每一项功能都要有对应的测试输入。测试的预期输出取自参考实现本身:把输入在参考实现上重复运行五遍,只保留五次结果都一致的部分作为比对依据,从而排除运行之间本就会变化的内容。时间戳、临时文件、耗时信息等无法稳定复现的内容不计入评分,被测的 Coding Agent 因此不必重现本身就不确定的输出。
- 回查覆盖情况。测试准备完毕后,该 Agent 逐项核对清单,确认每一项功能均有相应的测试覆盖。核对结果与尚未覆盖的条目一并记录在任务的元数据中,供人工复核;发现遗漏即退回上一步补充测试。
这样得到的测试以参考实现的实际行为为依据,而不取决于出题者的经验或直觉。评测时逐条比对候选实现与参考实现的输出;仓库级任务的一条用例是一串命令,其中每条命令的退出码、标准输出、标准错误与文件系统状态都要分别核对。功能测试不设部分得分,全部用例完全一致才判为通过。
性能测试:如何测得可信的加速
加速比记为 r = c(S) / c(S′),其中 c(·) 是评测程序测得的执行开销,而非候选程序自行输出的数值。多数任务以墙钟时间衡量开销,少数运行于封闭模拟器中的任务则以模拟得到的指令周期数衡量,该数值每次运行均相同,不受运行环境波动影响。
计时的主要困难在于噪声:同一份代码在两次运行中的耗时本身即存在波动,单次测量结果不足以证明性能确有提升。评测程序为此采取三项措施:
- 成对交错运行。参考实现与候选实现在同一批输入上成对执行,且先后顺序逐次交替,使 CPU 降频、相邻进程争抢资源等扰动同等作用于两侧。测得的加速比因而反映两份实现之间的差异,而非运行环境的起伏。
- 计时前预热。正式计时之前先执行若干轮不计入结果的运行,使缓存与编译进入稳定状态。
- 短函数循环调用。对于执行时间过短、难以可靠计时的函数,在一次测量内循环调用多次,使总时长超出计时器的精度下限。
若重构前后的耗时差异落在多次测量的波动范围之内,则视为没有加速。加速须达到的幅度由每道题的题面规定,并须在重复测量中稳定成立。
合规与防作弊
功能测试与计时都以候选实现按规则参与评测为前提。为使这一前提成立,我们在两项测试之外增设两项措施:
- 源码检查。在功能测试之前进行。每道题都给出一份清单,列明允许使用的引用与禁止调用的函数;评测程序逐一检查提交代码中出现的引用与函数调用,与清单对照。一旦出现禁止项,该次提交即计零分,后续的功能测试与计时不再执行。
- 运行隔离。在计时环节生效。参考实现与候选实现分别在两个权限受限的账户下运行,各自可创建的进程数受到限制,无法将计算移交其他进程以规避计时;对一方计时期间,另一方的进程被暂停,两者均无法通过占用处理器资源延长对方的耗时。
两项措施都不涉及模型判断:规则由题面固定,同一份提交每次得到的结论相同。
综合起来,一次提交需要通过的判定共有三项:功能、性能与合规。三项全部通过的提交,本文记为严格通过。报告结果时,功能与性能各自单独列出,不合并成一个总分:正确而未变快、变快而有错误,是两类不同的失败,只有分列记录才能在结果中区分开。
FrontierRefactor Preview-30
上述设计已在一批任务上落地。我们首先公开其中的 30 个,构成 FrontierRefactor Preview-30,四个尺度的数量分别为 Kernel 级 3 道、模块级 17 道、包级 8 道、仓库级 2 道。
| 尺度 | 代表任务 | 初始代码 | 题面要求 |
|---|---|---|---|
| Kernel 级 | vliw-kernel-scheduling | 一个为 VLIW 处理器生成指令序列的生成器。每个指令包只放一条指令,且全程只使用标量运算,向量单元始终空闲 | 改写生成器,在满足数据依赖与每包容量限制的前提下,用更少的周期完成同样的计算。该任务在封闭的模拟器中运行,以模拟得到的周期数计分 |
| 模块级 | iterable-shard-skipping | 一个从有序分片中跳过前若干条记录、再取出至多指定条数的函数。它先把所有分片拼接成一份完整列表再切片,而拼接时每次都会复制一遍已累积的内容 | 优化这段代码,同时保持公开函数签名与可观察行为不变,包括返回值类型、元素顺序、异常行为,以及输入是否被原地修改 |
| 包级 | commonmark | 一个 Markdown 处理库,公开接口包括将 Markdown 渲染为 HTML 与解析为结构化语法树,覆盖完整的块级与行内语法 | 从零重新实现整套接口,并给出更快的版本,语言不限。原实现可以阅读和移植,但运行时不得导入、链接或调用任何等价实现 |
| 仓库级 | rtk | 一个用 Rust 编写的命令行工具。它在 ls、tree、cat、grep、git 等命令之上再封装一层,将这些命令的输出压缩为更紧凑的形式,便于模型读取 | 用 Go 重新实现整个工具,行为需与原实现完全一致。运行镜像中没有安装 Rust 工具链,原实现无法被编译或调用 |
实验结果
实验设置
被测对象为 GPT-5.5,推理强度设为 medium,由 Codex CLI 0.145.0-alpha.27 驱动;任务的封装与运行由 Harbor 0.18 承担。30 道任务各进行三次独立尝试,共 90 次。每次尝试均从任务给定的初始工作区重新开始,此前尝试的过程与结果不带入下一次,三次结果因此相互独立,可用于考察同一道题上表现的稳定性。
逐任务结果
表 3 至表 6 按 Kernel 级、模块级、包级、仓库级四个尺度,分别给出每道任务三次尝试的结果,各列含义如下。
功能:是否通过功能测试,✓ 为通过,✗ 为未通过。
加速比:r = c(S) / c(S′),由评测程序测得;未通过功能测试的尝试不进入计时,记为 N/A,三次都未通过则整格记为 —。
严格通过:功能、性能与合规三项是否全部通过。
步数:该次尝试中 Agent 与环境交互的次数。每一次交互包含一轮模型输出及其触发的工具调用,因此步数反映 Agent 在这道题上反复修改的次数。
| 任务 | 功能 | 加速比 | 严格通过 | 步数 |
|---|---|---|---|---|
| vliw-kernel-scheduling | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 25 / 18 / 18 |
| structured-array-conversion | ✓ ✓ ✓ | 1.00 / 1.01 / 1.00 | ✗ ✗ ✗ | 8 / 9 / 9 |
| tensor-output-layout | ✓ ✓ ✓ | 2.03 / 2.00 / 2.06 | ✓ ✓ ✗ | 12 / 9 / 8 |
| 任务 | 功能 | 加速比 | 严格通过 | 步数 |
|---|---|---|---|---|
| tabular-dict-construction | ✓ ✓ ✓ | 0.99 / 0.99 / 1.00 | ✗ ✗ ✗ | 6 / 11 / 5 |
| iterable-shard-skipping | ✗ ✓ ✗ | N/A / 299 / N/A | ✗ ✓ ✗ | 11 / 11 / 14 |
| generic-model-creation | ✓ ✓ ✓ | 0.64 / 1.11 / 0.07 | ✗ ✗ ✗ | 7 / 3 / 2 |
| array-equality | ✓ ✓ ✓ | 1.00 / 0.99 / 1.00 | ✗ ✗ ✗ | 7 / 5 / 12 |
| gif-frame-counting | ✓ ✓ ✓ | 0.03 / 0.03 / 1.02 | ✗ ✗ ✗ | 1 / 0 / 4 |
| multi-level-exact-lookup | ✓ ✓ ✓ | 0.82 / 0.78 / 0.44 | ✗ ✗ ✗ | 11 / 12 / 17 |
| contiguous-data-selection | ✓ ✓ ✓ | 0.67 / 0.88 / 0.01 | ✗ ✗ ✗ | 17 / 12 / 14 |
| integer-range-membership | ✓ ✓ ✓ | 0.99 / 0.98 / 0.97 | ✗ ✗ ✗ | 10 / 10 / 9 |
| constrained-decoding | ✓ ✓ ✗ | 6.76 / 4.34 / N/A | ✓ ✓ ✗ | 9 / 14 / 10 |
| token-preprocessing | ✓ ✗ ✗ | 1.00 / N/A / N/A | ✗ ✗ ✗ | 12 / 7 / 17 |
| rotary-cache-update | ✓ ✓ ✗ | 5.21 / 5.23 / N/A | ✓ ✓ ✗ | 22 / 12 / 15 |
| numeric-fuzzy-matching | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 5 / 13 / 7 |
| tree-enumeration | ✓ ✗ ✗ | 2.71 / N/A / N/A | ✗ ✗ ✗ | 19 / 16 / 24 |
| attribute-dispatch | ✓ ✓ ✓ | 1.01 / 1.04 / 1.00 | ✗ ✗ ✗ | 11 / 5 / 7 |
| stateful-method-cache | ✗ ✗ ✓ | N/A / N/A / 1.76 | ✗ ✗ ✓ | 16 / 7 / 7 |
| async-write-buffer | ✓ ✓ ✓ | 0.94 / 0.95 / 0.95 | ✗ ✗ ✗ | 8 / 5 / 10 |
| xml-serialization | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 12 / 9 / 15 |
| 任务 | 功能 | 加速比 | 严格通过 | 步数 |
|---|---|---|---|---|
| commonmark | ✗ ✓ ✗ | N/A / 1.47 / N/A | ✗ ✗ ✗ | 24 / 36 / 42 |
| cssselect | ✓ ✓ ✓ | 26.67 / 1.53 / 1.44 | ✓ ✓ ✗ | 26 / 20 / 23 |
| graphql-core | ✓ ✗ ✗ | 9.67 / N/A / N/A | ✓ ✗ ✗ | 46 / 30 / 38 |
| idna | ✓ ✓ ✓ | 33.96 / 31.96 / 30.50 | ✓ ✓ ✓ | 21 / 27 / 25 |
| netaddr | ✓ ✗ ✗ | 16.10 / N/A / N/A | ✓ ✗ ✗ | 40 / 37 / 23 |
| packaging | ✓ ✓ ✓ | 3.10 / 4.71 / 0.64 | ✓ ✓ ✗ | 25 / 26 / 20 |
| pygments | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 49 / 31 / 49 |
| sqlglot | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 25 / 29 / 24 |
| 任务 | 功能 | 加速比 | 严格通过 | 步数 |
|---|---|---|---|---|
| rtk | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 39 / 28 / 44 |
| uv-pip-compile | ✗ ✗ ✗ | — | ✗ ✗ ✗ | 24 / 44 / 41 |
结果分析
多数尝试通过了功能测试,但只有少数达到加速要求
重构任务要求同时满足两项条件:保持原有功能,并提升运行速度。为比较两者的难度,我们对 90 次提交依次施加三道判定:能否通过功能测试、耗时是否确实缩短、缩短的幅度是否达到该题要求。如图 2 所示,90 次提交中 53 次通过了功能测试,其中 23 次耗时确实缩短,最终 17 次达到该题要求的幅度。第一道判定排除了 37 次,后两道又排除了 36 次。
在后两道判定中被排除的提交里,有相当一部分并未停留在原有水平:53 次功能通过的提交中,14 次比参考实现更慢,另有 16 次的耗时变化过小,无法与测量噪声区分。async-write-buffer 三次分别为 0.94×、0.95×、0.95×,multi-level-exact-lookup 为 0.82×、0.78×、0.44×,行为完整保留,运行反而变慢;这类结果只有计时环节能够发现。
另一方面,通过全部三道判定的那 17 次,也大多集中在少数任务上。图 2 最右侧给出判定逐级收紧时仍能达成的任务数:30 道任务中 23 道至少被正确解决过一次,取得严格通过的降至 10 道,三次尝试全部严格通过的仅有 idna 一道。iterable-shard-skipping 测得 299×,为整套任务中幅度最大的一次提升,但另外两次尝试未能完整复现迭代语义。在一次尝试中找到有效的重构方案,与在每次尝试中都能找到,对应的是不同的能力水平。

代码规模扩大后,功能通过率下降,可获得的加速幅度上升
从 Kernel 级到仓库级,被重构的代码规模逐级扩大。为观察表现随代码规模的变化,我们对四个尺度分别统计三项数据:功能测试的通过率、完成一次尝试所用的步数,以及测得的加速幅度。如图 3 所示,功能通过率并非逐级下降,而是在某一处骤降:Kernel 级与模块级分别为 67% 与 69%,包级降至 50%,仓库级的 6 次尝试全部未通过。下降出现在代码规模由单个函数或例程扩大到整套公开接口的这一步上。
仓库级的 6 次尝试虽然全部未通过,却并非全盘出错,而是始终留有少量偏差。按前文所述,仓库级的一条用例内部要逐条核对多项结果,两道题合计的核对项分别为 5588 项与 160 项:rtk 三次尝试分别通过 4191、4191、4318 项,uv-pip-compile 三次通过 120、120、110 项。
第二项数据是完成一次尝试所用的步数,它同样随代码规模上升,说明大尺度任务需要更多轮交互:90 次尝试的步数中位数为 14,Kernel 级与模块级分别为 9 与 10,包级升至 26.5,仓库级为 40。但步数多寡只与任务规模相关,并不预示单次尝试能否成功:模块级中三次尝试平均步数最高的 tree-enumeration 为 19.7 步(19、16、24 步),三次尝试中仅一次通过功能测试且未达到加速要求,而幅度最大的 299× 只用了 11 步。步数过少则暴露另一个问题:gif-frame-counting 的前两次尝试分别只用了 1 步与 0 步,提交的代码加速比均为 0.03×,比参考实现慢三十倍以上。Agent 在重构过程中没有自行计时,无法判断一次改动究竟是优化还是劣化。
尽管包级更难通过功能测试,但通过之后测得的加速明显更大。前述 23 次耗时缩短的尝试中,包级占 11 次,加速比的中位数为 9.67×;模块级占 9 次,中位数为 4.34×,其中 idna 三次稳定在 30× 以上,cssselect 与 netaddr 分别达到 26.67× 与 16.10×。包级不限定实现语言,但 24 次提交无一改用其他语言,加速全部来自在 Python 内重写整套接口:以 netaddr 为例,参考实现用一组类表示地址与网段,重写后的版本直接以整数表示地址,用标准库的解析函数替换逐层的对象构造。包级任务考察的不再是局部优化技巧,而是在整体重写中同时保证正确性与性能。

未通过功能测试的尝试之间,通过的测试用例比例差异显著
功能测试只给出通过与不通过两种结果,但差一条用例与差一半用例,对应的后续处理并不相同。包级与仓库级共 30 次尝试,其中 18 次未通过;如图 4 所示,7 次的通过比例不足 70%,7 次落在 70% 至 99% 之间,另有 4 次在 99% 以上。
这 18 次中最接近通过的是 netaddr,第二、三次尝试都通过了 1567 条用例中的 1564 条,仅差三条,在原有实现上继续修正就有可能通过;另一端是 sqlglot,三次尝试的通过比例都在 53% 以下,实现思路从一开始就有偏差,需要重新设计。逐条记录用例的比对结果,方可判断一次失败与通过之间的距离,进而决定后续应继续修正还是重新设计。

同一份实现在不同输入上测得的加速并不一致
表 3 至表 6 中每次尝试只列出一个加速比,但一道任务通常配有多组计时输入,它们之间的差别可能是同一操作的数据量不同,也可能是操作本身不同。我们对每组输入单独计时、分别记录,不将结果合并。如图 5 所示,包级共有 12 次提交通过功能测试并进入计时。其中 commonmark 的第二次尝试通过了全部 1305 条功能测试,渲染一组输入的加速为 2.97×,解析另一组仅为 1.47×,后者未达到该题 1.5× 的要求。我们按其中最差的一组计分,这次提交因此不计为严格通过。若改取两组的平均,则为 2.2×,足以记为达标;一组输入上的不足会被另一组的优势掩盖,所得数值不再对应任何一种实际调用方式。

后续工作
我们将从三个方向继续推进这项工作。
扩充任务集。在覆盖范围与难度上同时展开:纳入更多编程语言、更多应用领域,以及规模更大的重构场景;同时提高单道任务的优化难度,使其更接近真实工程中需要反复权衡的情形。
跟进前沿模型。代码能力仍在快速变化,我们会持续把新发布的模型纳入评测,按本文所述的同一套标准运行,以便不同时期的结果可以直接对照。
发布公开排行榜。我们将把各模型在 FrontierRefactor 上的成绩整理为公开排行榜,并随新模型与新任务的加入持续更新。