功能价格文档博客
获取 mote
← 博客
工程·2026年8月21日·阅读需 5 分钟

让应用仅仅保持打开时不占用 CPU 的方法

编辑器虽然一整天都开着,但真正打字的时间只占其中一部分。mote 在其余时间里既不调度帧,也不设置定时器,并通过代码检查和真机测量两道关卡来确保这一点。

m
mote
运行一次门禁检查后的输出。使用 dev flavor 打开 100 KB 文档,并在真机 GPU 上测得:窗口出现后的 10 秒内绘制帧数为 0,空闲 CPU 使用率为 0.10%。
运行一次门禁检查后的输出。使用 dev flavor 打开 100 KB 文档,并在真机 GPU 上测得:窗口出现后的 10 秒内绘制帧数为 0,空闲 CPU 使用率为 0.10%。

编辑器是笔记本电脑上打开时间最长的应用。但真正用于写作的时间并不长。大多数时候,它只是在你做其他事情时安静地开着。

因此,mote 定下了一条原则。

如果文档没有变化,也没有动画,就什么也不做。

没有预定帧,没有重复定时器,也没有周期性检查。这不是优化,而是一条必须优先于所有功能遵守的设计原则。

如果无事发生,帧数也为 0

仅凭 CPU 使用率很难判断应用是否安静。因为每次测量时数值都会波动,而且经过四舍五入后很容易变成 0。

所以 mote 首先看帧数。窗口打开后,如果文档在 10 秒内没有变化,绘制的帧数就必须恰好为 0。帧数直接在应用内部计数,只要绘制了一帧,代码就不会被合并。

即使帧数为 0,也不能放心。因为线程可能会在看不见的地方不断被唤醒。我们也会同时记录进程中的所有线程每秒被唤醒多少次。由于目前还在积累基准,因此不会仅凭这项数值阻止开发。

唯一的例外是插入符闪烁。它不使用重复定时器,而是在闪烁一次后重新调度下一次动作。窗口一旦失去焦点,就会立即停止。包括插入符在内的空闲 CPU 预算是 0.3%。

我们不会只单独看空闲性能。推迟绘制虽然很容易让帧数变成 0,但输入的文字可能会延迟出现。因此,我们会同时检查按键输入延迟、启动速度和内存。只要有一项超过标准,代码就不会被合并。

预算表中的 hard 项目——空闲帧 0 / 10 s、空闲 CPU 0.3%、从外部测得的按键→像素 p95 33.4 ms、从内部测得的按键处理 p95 4 ms、冷启动中属于我们的部分 10 ms、内存中属于我们的部分 8 MB。最下面一行写着四项测量条件
预算表中的 hard 项目——空闲帧 0 / 10 s、空闲 CPU 0.3%、从外部测得的按键→像素 p95 33.4 ms、从内部测得的按键处理 p95 4 ms、冷启动中属于我们的部分 10 ms、内存中属于我们的部分 8 MB。最下面一行写着四项测量条件

在代码中检查一次,在真实设备上再测一次

原则会随着时间推移而变得模糊。所以我们会确认两次。

首先检查代码。自动查找重复定时器、永不结束的 tick、反复休眠和唤醒的轮询,以及不必要地重新读取整个文件的代码。一旦发现,CI 就会在那里停止。如果确实是必需的例外,就必须在代码正上方说明为什么该任务会结束。

接下来在真实设备上测量。这时要遵守四项条件。

在配有 GPU 的真实屏幕上测量。指针放在窗口外。使用真实的会话总线,并在帧被绘制的瞬间立即计数。

这些条件都是经历失败后形成的。曾经仅仅因为指针停在窗口上,就产生了 150 个空闲帧。在隔离的会话中,工具包会因为等待响应而使启动延迟数秒。使用延迟上报的帧计数器时,启动画面的帧还曾混入空闲区间。

内存也不按绝对值判断。因为工具包和图形驱动程序占用的内存会随环境变化数十 MB。

我们改用空白文档作为基准。只看打开文档后比空白文档多用了多少内存。最近一次测量中,空白文档使用了 116MB,100KB 文档使用了 120MB。mote 额外使用的内存是 4MB,处于 8MB 的预算之内。

一开始并不是这样测量的。我们曾以为超出了内存预算,后来才发现增加的并不是应用的内存,而是运行环境的基础内存。从那天起,我们开始测量的就不再是绝对值,而是“我们的功能所增加的部分”。

门禁检查使用的 100 KB 固定测试文档以实时模式打开的画面——底部状态栏正在统计 20,584 个单词和 96,513 个字符。这个文档与空白文档之间的差值,就是预算所说的“属于我们的部分”
门禁检查使用的 100 KB 固定测试文档以实时模式打开的画面——底部状态栏正在统计 20,584 个单词和 96,513 个字符。这个文档与空白文档之间的差值,就是预算所说的“属于我们的部分”

开发时,屏幕上还会显示仍在运行的定时器数量。如果不是 0,就能在提交前立即发现。

自动保存也遵循这项原则。只有文档发生变化时才设置一次定时器,保存完成后就将其移除。它不会周期性地唤醒并检查“有没有发生变化?”

这些数字不是为了战胜竞争对手

一开始,我们想把空闲性能作为优于 Typora 的特点来介绍。但亲自测量后,发现事实并非如此。

在相同设备和相同条件下,Typora 的空闲 CPU 使用率是 0.1%。实际上与 mote 相同。确认存在差异的应用是 Obsidian,为 0.8%;MarkText 为 0.6%。

我们也曾写错过。有一段时间,文档中写着“Typora 开着时会占用 CPU”。作为依据的 34.6% 并不是 Typora 的测量值,而是 apostrophe 的测量值。重新确认后,我们立即修改了这句话。

空闲帧数为 0 并不是针对竞争对手的主张。它是 mote 对自身施加的纪律。因为如果没有反复运行的任务,我们就能随时解释应用此刻正在做什么。

因为这项原则,我们也放弃了一些东西

很多时候,我们不能采用方便的方法。

不能通过周期性检查来判断文件是否在外部发生了变化,而必须接收操作系统的变更通知。不能把公式交给 WebView,而是亲自开发了布局引擎。动画也不能一直运行。所有动作都有开始和结束,结束后必须完全停止。

我们不会声称电池可以多续航几个小时。因为那是没有测量过的数字。我们确认过的只有帧数、CPU 使用率,以及线程被唤醒的次数。

无事发生时,什么也不做。

在 mote 中,这不是一项功能,而是所有功能都必须通过的原则。

← 较早制定一次购买即可在整个 1.x 版本周期内使用的价格的理由较新 →不说“轻量”,而是公布具体数据的理由
通过RSS持续关注。
只留下文字
简体中文
© 2026 mote