结论先放这里。cvxgenrust 值得看,但别冲着性能去——论文自己报的数据里,共有类别上它跟 CVXPYgen 打平,没有那个让你眼睛一亮的加速倍数。它真正的卖点就两个:生成的是一整个 Rust crate,不是 C;这个 crate 还能注册回 CVXPY 当自定义求解器,建模端一行不用改。已经在 Rust 生态里的,优先看它;只是想给 Python 里的 CVXPY 提速的,它跟 CVXPYgen 的差异基本不在性能上,在迁移成本上。
评测条件先说清楚,不然后面的数字全是裸数字。我读的是 arXiv:2609.13875 v1,2026 年 9 月 12 日提交,作者 Hao Zhu 和 Joschka Boedecker。benchmark 没独立复现,下面转述的性能数据全是论文自报,条件以论文原文为准。分类挂在 math.OC,标了 cs.CE、cs.LG、cs.MS,DOI 页面还写着“尚待注册”。v1 的东西,有些结论我等后面版本再对。
有一个判断不依赖具体数字:参数化问题代码生成的加速方向是确定的。CVXPY 里每次 solve,都要重新做 DCP 分析、规范化、变量排序、转标准形、再交给底层求解器。cvxgenrust 把这些全挪到生成阶段——问题族规范化、仿射映射提取、转成 Clarabel 的锥规划数据——一次做完,运行时只剩更新参数和调求解器两件事。这跟每次 solve 都重新建模比,复杂度完全不在一个量级。论文报的具体加速倍数我不转述,条件没逐项核对;方向不需要怀疑。
维度表先摆出来,评分逻辑在你想杠的地方我说明一下。
| 维度 | cvxgenrust | CVXPYgen | 直接 CVXPY 求解 | 手写 Rust + Clarabel |
|---|---|---|---|---|
| 运行时性能 | 生成后低,论文自报与 CVXPYgen 相当 | 生成后低,与前者相当 | 每次重新建模,慢 | 理论最快,人力成本最高 |
| 生成代码语言 | Rust | C | 无 | Rust |
| Python 集成 | 注册为 CVXPY 自定义求解器 | 有 Python 接口 | 原生 | 需手写绑定 |
| 问题覆盖 | 半定规划、指数锥 | 与前者有交集,边界各自文档为准 | 几乎全谱 | 取决于你自己写多少 |
| 工程成本 | 中:建模在 CVXPY,生成自动 | 中:建模方式不同但生成自动 | 低:直接解 | 高:全部手写 |
| 可审计性 | 生成的 Rust 代码可读可查 | 生成的 C 代码可读,审查门槛高 | 无生成代码 | 看团队能力 |
这张表里没有赢家。Python 集成那一栏,cvxgenrust 最值得点一下:它把生成的东西注册成 CVXPY 自定义求解器。这意味着你建模端不用动——还是 cp.Problem 和 constraints,solve 时把 solver 参数切过去就完事。对生产里已经用 CVXPY 建模的团队,试用这个工具的迁移成本几乎为零。这也是它跟 CVXPYgen 在工程体验上一个容易被忽略的差别:CVXPYgen 的建模接口跟 CVXPY 不完全是同一套,切换不是只换一个参数。
同一件事,代码上看差异更直接。同样是解一个参数化问题,直接 CVXPY 解和挂生成求解器的前端几乎一样:
# 直接 CVXPY:每次 solve 都重新建模
prob = cp.Problem(cp.Minimize(objective), constraints)
result = prob.solve(solver=cp.CLARABEL)
# cvxgenrust 生成之后:前端不变,solver 切过去
prob = cp.Problem(cp.Minimize(objective), constraints)
result = prob.solve(solver=CVXGENRUST_SOLVER) # 运行时只更新参数
在 Rust 服务里调用生成的 crate 也是同样直接:
// 生成的 crate 在线更新参数,不用跨语言走 Python 子进程
let mut solver = order_exec_solver::new()?;
solver.update_params(&new_params)?;
let result = solver.solve()?;
这三段是示意代码,不是从某个仓库里扒出来的,但差别方向是准的。前端真的是零改动,这也是我把它列为 Python 场景下首选的主要原因。
机制拆开看,它干的事是把“建模”和“求解”在生成阶段焊死。问题族规范化、仿射映射提取、转锥规划数据,全都发生在生成的时候。之后运行时更新的只是参数——参数变,问题结构不变。这正是参数化凸优化的典型场景:模型预测控制里同一套动力学、不同的状态和输入;金融里同一组约束、不同时刻的市场数据。结构固定、数据反复问答案,把建模开销摊销掉是稳赚的 trade-off。
代价是明摆着的:代码生成不适合结构乱变的场景。如果你每次 solve 的约束数量、变量维度、锥体结构都不一样,生成出来的 crate 要么没法用,要么得为每一种结构生成一份,维护负担直接爆炸。所以 cvxgenrust 的正确打开方式是“问题族固定、参数频繁变”,不是拿它当通用加速器去解一次性问题。脱离场景谈优劣是耍流氓对比,代码生成类工具尤其如此。
Rust 这一步,CVXPYgen 输得不冤。CVXPYgen 生成的是 C 代码,老牌、稳定,嵌入式领域用了很久。但 C 生成的代码有一个很现实的问题:审查边界。数组越界、类型不匹配、内存问题,往往要到运行期甚至数值爆炸之后才冒出来。Rust 生成的 crate,这些问题在编译期就能被抓住一大半——参数维度不匹配、类型错误这种低级但常见的坑,cargo build 直接报。对已经在 Rust 里写东西的团队,生成一个能直接进 workspace 的 crate,比对着一堆生成的 C 文件写绑定、再在 CI 里挂 sanitizer 提心吊胆,体验差得不是一点半点。C 代码的审查压力我是真体验过,每次 CI 里跑一堆检查想骂人。
但反过来,如果团队里根本没人写 Rust,这个卖点对你就没有意义。生成的 Rust 代码就是个黑盒,跟生成 C 没什么区别——甚至更麻烦,因为你连改一行都下不了手。所以性能打平这件事本身就意味着:它的目标用户不是通用凸优化人群,是已经在 Rust 里、又不想从 Clarabel 裸写的人。对那部分人,这个工具的价值是实打实的,跟性能无关。
问题覆盖这一栏,cvxgenrust 的声明比很多工具宽。它声称覆盖到半定规划和指数锥。这个覆盖面在代码生成类工具里算宽的——很多工具到 SOCP 就停了,SDP 和指数锥直接不碰,因为转锥数据的复杂度上去了。能覆盖到这两类,说明作者在 Clarabel 的接口上做得比较完整,不是只挑好做的锥体做个玩具。不过这条我也是按论文自报的理解来的,没自己拿生成出来的代码去验。等它再迭代几个版本,我会找一组 SDP 的例子实际跑一下,看看到底是覆盖还是擦边。这条我只在论文声明的条件下成立,实际覆盖边界待验证。
场景化建议按人分。Python 里建模、问题族固定、参数频繁变、想提速又不想离开 CVXPY 生态的——cvxgenrust 是当前这个场景下首选。前端不变,后端换掉,风险最低。已经在 Rust 里写优化相关服务、需要内嵌凸优化求解器、参数在线更新、没法频繁走 Python 子进程的——生成的 Rust crate 是这波工具里最顺手的。这个场景下我不建议你去绑 CVXPYgen 的 C 代码。问题结构每次都变、没有固定问题族的——别上任何代码生成工具,直接 CVXPY 解,或者老老实实按问题规模找专用的求解器。嵌入式或者 RTOS 上跑、要的是轻到极致的 C 代码的——CVXPYgen 还是这个场景下更成熟的选择。Rust 生成的东西有没有在 bare metal 上验证过,这篇 v1 论文没提,我不能替作者打包票。
补一句公正补丁。没出现在推荐里的不代表差,是场景不匹配。CVXPYgen 在嵌入式 C 代码生成这条路线上积累的成熟度,cvxgenrust 目前还比不上;Rust 这个方向很有劲,但 v1 阶段没有大规模部署数据,生成代码的质量、可维护性、极限规模下的表现,都还需要时间验证。这个结论是 v1 版本下的,等作者或社区把 SDP 的实际案例跑出来、或者有大一点的参数规模数据了,我会拿过来重新对一遍——我盯着呢。
写作过程中我犹豫过一个地方。性能打平这件事,很多人会失望,觉得“Rust 版的和 C 版的没什么差别,那写出来干嘛”。我倒觉得这恰恰是正常的工程产出:语言栈切换本来就不该带来数量级的性能飞跃,性能飞跃来自算法和数值实现,不来自你生成 Rust 还是 C。把 Rust 生态的接入做成一项实实在在的收益,比硬吹一个不存在的 20% 加速诚实得多。这篇论文在这点上没水分,数据该持平就持平,不粉饰,就凭这点我应该给它一句肯定。它把编译路径又往前推了一步——推得不算惊艳,但方向是对的。够了。
