性能优化
前言
文字游戏的性能问题,通常不会表现为“掉帧”,而是每次输入后要等一两秒才响应。这一篇讲讲卡顿从哪来、怎么找、怎么改。
先测量,再优化
不要凭感觉猜。Emuera 提供了计时函数:
erb
#Dim T0 = 0
#Dim T1 = 0
GETMILLISECOND
T0 = RESULT
; ……要测量的处理……
GETMILLISECOND
T1 = RESULT
PRINTFORML 耗时 {T1 - T0} 毫秒先量出到底是哪一段慢,再动手。大多数时候,瓶颈都集中在少数几个地方。
瓶颈一:循环
这是最常见的性能来源。几条经验:
用内置命令代替手写循环
同样的“批量操作”,内置命令通常快得多:
| 需求 | 手写循环 | 推荐 |
|---|---|---|
| 给数组批量赋值 | FOR 逐个赋值 | VARSET / CVARSET |
| 排序 | 自己实现冒泡 / 快排 | ARRAYSORT |
| 复制数组 | FOR 逐个复制 | ARRAYCOPY |
| 平移 / 删除元素 | FOR 手动挪动 | ARRAYSHIFT / ARRAYREMOVE |
例如给 FLAG 全部清零,写 VARSET FLAG, 0 远比 FOR … NEXT 快。
减少循环内的重复计算
把不变的东西挪到循环外面:
erb
; 不好:每次循环都重复求 VARSIZE
FOR I, 0, VARSIZE("FLAG")
...
NEXT
; 更好:只在循环外求一次
#Dim N = 0
N = VARSIZE("FLAG")
FOR I, 0, N
...
NEXT尽量早退出
找到结果就用 BREAK / RETURN 结束,别把整个数组都遍历完。
瓶颈二:字符串
- 少在循环里做大量的
+拼接,累积成一段再一次性输出; - 避免在循环里反复调用
STRLEN/STRLENS等度量函数,能提前算好就提前算; - 需要重复显示同一段内容时,缓存到字符串变量里。
瓶颈三:绘制
フレーム毎秒(每秒帧数)越小,绘制越省;牺牲一点动画流畅度换取响应速度,往往是值得的;- 不需要每帧更新时,用
REDRAW 0暂停自动重绘,只在需要输入时刷新; - 避免在循环里反复
CLEARLINE+ 重绘整屏。
耗时任务:分批 + AWAIT
如果某一步确实要算很久(例如初始化大量数据),不要让它“卡死”界面。可以边算边显示进度:
erb
REDRAW 0
FOR I, 0, 100
PRINTSL "处理中…… " + TOSTR(I) + "% 完成"
AWAIT
CLEARLINE 1
; 这一批的处理
NEXTAWAIT 会暂停 ERB 执行、让 Windows 处理消息,避免窗口“无响应”。但也不要调用得过于频繁,否则反而变慢。
一个反直觉的建议
可读性优先于微优化。
绝大多数文字游戏,经过上面的调整后性能都绰绰有余。为了省几毫秒把代码写得晦涩难懂,通常是得不偿失的——真正需要死抠性能的场景,其实很少。
小结
- 先用
GETMILLISECOND测量,再优化; - 循环是主要瓶颈:优先用内置的批量命令;
- 把循环内不变的计算挪出去,尽早
BREAK/RETURN; - 减少字符串拼接与重复度量;
- 用
REDRAW、フレーム毎秒控制绘制开销; - 耗时任务分批处理并配合
AWAIT。