px普贤教程PuXian
普贤教程 › 进阶教程

性能与陷阱

PuXian 的性能问题几乎都能归到一句话:你在解释轨还是编译轨。搞清这一点,剩下的坑都是有解的。

实测:同一个循环,解释轨比编译轨慢约 500 倍

px
var t0 = now_ms()
var sum = 0
for i in range(0, 100000):
    sum += i
print("耗时 " + str(now_ms() - t0) + " ms")
执行方式耗时
px run(解释轨)5182–5928 ms(三次)
px run --fast9–10 ms
px build 默认(VM 字节码轨)10–12 ms
px build --c(C 轨)4–5 ms
结论直接一点任何"循环里干活"的代码不要用解释轨跑生产。定时任务、cron 脚本、接口处理,一律 px build 或让 Ma 用 mode: fast 托管。

坑一:逐像素绘图

gfx.set_px 内部是 c[2][y*w+x] = color:嵌套下标赋值每次都把内层列表复制一份再写回。132×44 的画布写 6400 次,解释轨实测 3.85 秒;同样的写入直接作用在 canvas_pixels(c) 取出的列表上只要 0.89 秒;编译轨(含 --fast)4 ms。

做法解释轨编译轨
set_px 写 6400 次3.85 s4 ms
直写 canvas_pixels0.89 s<1 ms
拼 SVG 字符串0.09 s0.05 s
图形优先用 SVG纯字符串拼装,解释轨也是毫秒级,体积还小(3 KB vs 28 KB PNG)。前端 <img src="data:image/svg+xml;base64,..."> 直接吃。

坑二:bytes 是不可变的

px
var b = bytes("abc")
var c = bytes_set(b, 0, 65)   # 返回值是新对象
print(bytes_to_str(b))
print(bytes_to_str(c))
text
abc
Abc

忘记接返回值,看起来"改了但没生效"。逐字节修改还有性能问题:每次 bytes_set 都复制一份,几十万次就是几秒。做法是攒成列表最后一次性拼。

坑三:大块写入会挂

写法结果
write_all(fd, data) 一次写 >16 MiB卡死(实测 17 MB 挂住)
分块 write(每块 1 MiB)正常
write_file(path, bytes)写成 <bytes N> 字面量,不是原内容
write_bytes(path, b)正确

坑四:字符串操作对二进制不安全

split、replace 这类字符串函数遇到 NUL 字节会截断。40 MB 的 zip 用 split 处理,实测被切成 108 个字符。二进制一律用 bytes_* 族。

坑五:除法返回浮点

px
var avg = 7 / 2
print(str(avg))
print(str(int(avg)))
text
3.5
3

要整数用 //;位运算(>> &)只吃 int,浮点会报错。

坑六:保留字撞车

写法报错
var from = "a@b.com"E2001 期望变量名
var in = 1E2001 期望变量名
def env(...)(遮蔽内建)无限递归 → 段错误,无提示

坑七:json_parse 会终止程序

px
var v = json_parse("坏输入")    # 不可捕获,进程直接挂

一律 json_parse_opt。同理 int("abc")、hex_to_bytes 的畸形输入。处理外部数据前先校验。

坑八:print 就是 HTTP 响应

在 Ma 的 px_routes 里,print 写的是标准输出 = 响应体。处理中间打一行日志,客户端收到的是坏响应。日志走 /dev/stderr。

坑九:mode: auto 的首请求

Ma 的 px_routes 在 mode: auto 下,首次请求走解释轨(可能要 1 秒多),之后才用编译产物。用户感知是"第一次点特别慢"。对交互敏感的路由直接写 mode: fast。

性能优化清单

症状先查什么
整体慢是不是在跑解释轨
某个接口 1 秒+是否有数据库连接建立、外部请求、发信
CPU 100% 单核循环里做字符串拼接 / 逐字节操作
内存持续涨循环里 append 不释放;或 bytes 反复复制
偶尔慢一下首次编译、GC、磁盘抖动
先测量再优化用 now_ms() 打点,先找出是"一次慢"还是"每次慢"。PuXian 里没有 profiler,打点日志就是最实用的手段。