‹ Term

GPU 渲染有多快?

Term 的终端渲染改用 EGL/GLES3 实例化渲染器:在真机上实测的方法、数据与画面。

撰写于 · 更新于

4.3ms
GPU 帧时间 · 235 fps
3.4×
对比 CPU 路径
0.35ms
GL 绘制 / 帧
结果

两次实测、同一份语料,都通过内置的 Log frame stats 开发者开关采集。CPU 那一列是 2026-07 在 Pura 70 上的基线——CPU 路径已经删除,无法重测,所以保留原值;GPU 那一列是 2026-08 在 HBN-AL80 上的新实测。两台不同的手机,因此提速倍数只说明量级,不必较真到小数点后两位。

工作负载 CPU 路径 GPU 路径 提速
全屏滚动(firehose) 14.6 ms · 68 fps 4.26 ms · 235 fps 3.4×
慢速滚动(~5 行/帧) 12.8 ms · 76 fps 5.05 ms · 198 fps 2.5×
logFrameStats · CPU 列:Pura 70,2026-07 · GPU 列:HBN-AL80,56×39 网格,2026-08 · 彩色 ANSI-SGR 语料

GPU 路径的帧时间几乎与滚动量无关:每一帧都是一次完整重绘,而这对 GPU 来说很便宜。CPU 路径对滚动行数敏感的问题就此消失。

GPU 帧构成

每帧约 1400 个实例化四边形。字形只光栅化一次进入纹理图集,之后每帧仅重新发射网格实例。

阶段 平均 说明
build 1.82 ms CPU 侧:遍历网格 → 实例四边形 + 光栅化新字形入图集 + 上传
draw 0.35 ms GL 实例化绘制
swap 2.08 ms eglSwapBuffers(呈现)
帧总计 4.26 ms ≈ 235 fps
2026-08-29 在 HBN-AL80 上重测 · 1260×2312 上的 56×39 网格 · 1 440 帧饱和全屏滚动

消除两次搬运 14 MB 的内存操作:不再有影子位图的滚动位移(~6 ms),也不再有到 dmabuf 的拷贝(~5.6 ms),这 ~11 ms 就是全部的节省。GPU 直接在 EGL 表面上原地光栅化。

实测画面

语料是彩色 ANSI-SGR 混合中文、韩文与 ASCII,由基准脚本写入会话 TTY,无需触碰手机。

全屏滚动 firehose:满屏彩色 ANSI + 中文/韩文/ASCII 连续滚动
P1 · 全屏滚动:满屏全损重绘的连续滚动。
空闲状态:仅光标闪烁的部分呈现帧
P2 · 空闲:仅光标闪烁,局部呈现。
单行更新:8 Hz 打字,其余行逐像素保留
P3 · 单行更新:8 Hz 打字,逐像素保留上方行。
基准脚本

脚本自动化三个阶段(firehose 滚动 / 空闲闪烁 / 单行更新),通过向远程主机上会话的 TTY 写入实现:手机端零交互。

gpubench.sh
#!/bin/bash
T="${1:-/dev/ttys000}"

# 语料:彩色 ANSI-SGR + 中文/韩文/ASCII,400 行
corpus() {
  i=0
  while [ $i -lt 400 ]; do
    printf "\033[3%dm%04d 端末描画性能測定 中文渲染基准 가나다라마 \033[1mBOLD\033[0m ascii-abcdefghijklmnop %05d\n" \
      $((i % 8)) $i $((i * 37))
    i=$((i + 1))
  done
}
C="$(corpus)"

# P1 firehose 滚动 30s(满帧全损)
printf "\n\033[36m=== GPUBENCH P1: firehose scroll 30s ===\033[0m\n" > "$T"
end=$((SECONDS + 30))
while [ $SECONDS -lt $end ]; do printf "%s\n" "$C" > "$T"; done

# P2 空闲 60s(仅闪烁 → 局部呈现帧)
printf "\n\033[36m=== GPUBENCH P2: idle (blink only) 60s ===\033[0m\n" > "$T"
sleep 60

# P3 单行更新 20s(~8 Hz,打字形状的局部帧)
printf "\n\033[36m=== GPUBENCH P3: single-row updates 20s ===\033[0m\n" > "$T"
end=$((SECONDS + 20))
n=0
while [ $SECONDS -lt $end ]; do
  printf "\rtyping simulation %06d" $n > "$T"
  n=$((n + 1))
  sleep 0.12
done
printf "\n\033[36m=== GPUBENCH done ===\033[0m\n" > "$T"
如何复现
  1. 打开一个到目标主机的会话;把 gpubench.sh 拷到该主机,用 who -u 找到会话对应的 TTY。
  2. 运行 ./gpubench.sh /dev/ttysNNN(会话的 tty)。
  3. 抓取:hdc -t <target> shell hilog | grep "connrender: gpu",每 60 帧输出一条统计窗口。

有两件事决定一次实测有没有意义。要给数据流限速。直接 cat 并不是持续负载——这里 20 万行 / 28 MB 不到一秒就消费完了,之后每个统计窗口都只是光标闪烁的 2 fps;把那些窗口读成“渲染器只有 2 fps”,等于什么都没测到。要按一个已知的速率持续供数。要盯着频率。同一屏内容,每秒只画两次时约 11 ms/帧,持续负载下约 4.3 ms——2.5 倍的差距纯粹来自 DVFS。停顿后的第一帧该用 11 ms 这个数,持续负载该用 4.3 ms,两者不可混比。另外记得收起软键盘:键盘一弹,网格就从 56×39 缩到 56×19,所有数字都会变好看。