大部分人做语言,目标是更快。 这门语言的目标是更慢,而且慢得可测量、可复现、 写进 ADR。
指标是:比 CPython 慢至少两个数量级,也就是 ≥100 倍。
两个基线,都报
「比 CPython 慢多少倍」这句话本身是不完整的——你得先说清 算的是哪一段。所以我用两个口径分别测:
| 基线 | 口径 | CPython | 倍数 | 达标 |
|---|---|---|---|---|
| A | 只算执行(timeit,code object 预编译) |
0.37 微秒/次 | 686 倍(2.84 个数量级) | ✅ |
| B | 全流程(exec,含编译) |
24.4 微秒/次 | 12.1 倍(1.08 个数量级) | ❌ |
pyPython 自身约 255 微秒/次。
注意最后那一列。基线 B 没达标——12.1 倍, 离 100 倍差得远。
这张图我也把「未达标」标了出来:
为什么不把没达标那条藏起来
因为那是测量,不是宣传。
ADR-0001 里写了一条原则:测量不打算造假。 如果只报基线 A,结论会好听得多——「慢 686 倍,超额完成」。 但基线 B 才是用户真正感知到的速度:他敲一行代码、按回车、 等结果。那一段包含了编译,而编译是慢的大头。
只报 A 等于告诉用户一个他自己测不出来的数字。
为什么画「范围」而不是一根柱子
这是做图时最花时间的一个决定。我先采样了 14 轮,结果很说明问题:
| 被测对象 | 范围 | 离散度 |
|---|---|---|
| pyPython 自身 | 244 ~ 279 微秒 | 14%(稳) |
| 基线 A(timeit) | 0.368 ~ 0.85 微秒 | 120%(很不稳) |
| 基线 B(exec) | 20 ~ 35 微秒 | 59%(不稳) |
基线 A 抖了 120%。原因很简单:它本身只有零点几微秒, GC 一次、系统调度一次,分母就能翻倍。
这种情况下画「一根精确的柱子」是撒谎。所以图上画的是 中位数 + 实测范围,并且在图里直接写明采样轮数和 「次之间会有偏差」。
结论是稳的
虽然具体倍数在漂,但量级结论不漂:
- 基线 A:462 ~ 743 倍 —— 永远 ≥2 个数量级 ✅
- 基线 B:6.9 ~ 12.9 倍 —— 永远 <2 个数量级 ❌
所以「慢两个数量级」这个说法,在基线 A 的口径下成立, 在基线 B 的口径下不成立。这两句话我都要说。
那它靠什么慢下来的
不是靠「写得烂」。是靠每一步都绕一圈:
- 值不直接存,走一套自定义的
encode/decode(长度前缀编码,见 ADR-0003) - 任何值判断真假都要过一遍「真值密室」
- AST 每个节点单独走一遍求值
这些在正常语言里都是要被优化掉的。这里是特意保留的—— ADR-0002 写着:语法越简单,实现越应当啰嗦。
一门语言把语法减到极致,通常是为了让实现也简单。 这里反过来了:语法减到极致,实现却要故意绕远路。