皮肤不是一张图片:先分清静态和动态两类
皮肤商店里几十页的皮肤,从资源构成上看其实只有两类。
第一类是静态图片皮肤。它本质上是一组 PNG 或 WebP 图片,分别对应候选框的各个部分。加载之后这些图片驻留在内存里,大小基本等于图片文件解码后的大小。这类皮肤不产生持续开销,候选框显示和隐藏时也不会有额外的计算。
第二类是动态动画皮肤。它除了图片,还包含动画帧序列或者可以直接播放的动效资源。候选框显示时,这些帧需要按设定的频率逐帧刷新,这个刷新过程会持续占用 CPU 和 GPU。帧率越高、动画区域越大,开销越高。
问题在于,皮肤商店的预览图和列表并不标注类型。你只能从预览图是否在动来大致判断,但有些皮肤只在特定条件下播放动画,预览时不明显。下载之前很难判断它的实际开销。
还有一类容易被忽略:部分动态皮肤在候选框隐藏后,背景动画仍在继续渲染。这类皮肤的持续开销比「只在候选框显示时播放动画」的皮肤高一个量级,但外观上看不出区别。
动态皮肤的开销具体来自哪里
把动画从显示到呈现拆开,大致经过三个步骤,每一步都有成本。
第一步是帧解码。如果皮肤包里存的是压缩过的帧序列,每次播放前需要解码。有些皮肤做得比较好的地方在于,把解码结果缓存在内存里复用,只解码一次;做得不好的则每次都重新解码,CPU 占用会明显上升。
第二步是合成。解码后的帧要和候选框的其他元素(文字、边框、阴影)叠加在一起。这一步由 GPU 参与,动画区域越大,参与合成的像素越多。
第三步是提交刷新。动画帧按设定的帧率提交给显示层,每一帧提交都会唤醒一次渲染流程。帧率 30fps 意味着每秒提交 30 次,这个频率本身就会阻止 GPU 进入低功耗状态。
三步里,第一步影响的是内存和 CPU,后两步影响的是 GPU 和功耗。这也解释了为什么有些动态皮肤在内存占用上看不出明显差异,但用起来感觉更费电。
另外要说明一个容易搞混的点:皮肤包的文件大小和内存占用不是一回事。一个压缩得很好的动画皮肤可能只有几兆的文件,但解码后展开成帧缓存,内存占用会明显大于文件大小。反过来,一张高分辨率的大图皮肤文件很大,但解码后就是一张位图,内存占用相对固定。
三类皮肤的资源占用对比
把三类皮肤放在一起比较,差异主要集中在是否持续渲染这一项上。
| 对比项 | 默认皮肤 | 静态图片皮肤 | 动态动画皮肤 |
|---|---|---|---|
| 是否包含动画帧 | 否 | 否 | 是 |
| 候选框隐藏时是否仍渲染 | 不渲染 | 不渲染 | 部分皮肤仍渲染背景 |
| 内存占用来源 | 基础进程开销,可忽略 | 解码后的位图 | 解码后的位图加帧缓存 |
| 是否占用 GPU 合成 | 极低 | 低,仅静态合成 | 较高,按帧率持续合成 |
| 候选框弹出延迟 | 最低 | 略高,取决于图片尺寸 | 最高,需等待首帧准备 |
| 对笔记本续航的影响 | 可忽略 | 可忽略 | 有影响,取决于帧率与显示时长 |
表里最值得留意的是「候选框隐藏时是否仍渲染」这一行。这一条决定了皮肤的开销是「只在打字时发生」还是「常驻发生」。前者在正常使用中几乎感觉不到,后者则会持续占用资源。
要判断一款动态皮肤属于哪一种,可以在候选框隐藏的状态下观察任务管理器里的 CPU 占用。如果输入法进程在空闲时仍持续占用一两个百分点,说明背景动画还在跑。
怎么自己测皮肤的实际开销
皮肤资源在切换之后不会立刻从内存释放,通常要等主程序重启才会回收。所以测量必须包含一次完整的重启,否则会把上一个皮肤的开销算进来。
- 建立基准——切到默认皮肤,完全退出搜狗输入法主程序(包括后台进程),重新启动,打开任务管理器,找到输入法相关进程,记录「私有工作集」的数值。
- 记录空闲增量——切换到待测皮肤,再次完全退出并重启,静置两分钟不做任何输入,记录同一进程的内存数值,减去基准值。
- 记录输入峰值——连续输入约三十秒,期间让候选框反复弹出和隐藏,观察并记录这一过程中的内存峰值。
- 复位——测完切回原皮肤并重启,确认数值回到基准附近。
要看的指标是「私有工作集」而不是「工作集」。前者反映的是进程自己实际占用的物理内存,后者包含了与其他进程共享的部分,容易被系统缓存干扰,测出来不稳定。
测的时候要关掉其他大内存程序,尤其是浏览器。浏览器的内存占用波动很大,会干扰任务管理器的读数。另外不要连续测多个皮肤,每测一个都完整重启一次,中间留出几分钟间隔。
实测:四种皮肤在三种场景下的内存增量
下面这组数据是一次人工构造的观察,用前面那套方法测出来的。
| 皮肤类型 | 空闲增量 | 输入峰值增量 |
|---|---|---|
| 默认皮肤 | 0 MB | +8 MB |
| 静态图片皮肤 | +7 MB | +16 MB |
| 低频动画皮肤 | +24 MB | +52 MB |
| 高频动画皮肤 | +58 MB | +121 MB |
数字的量级关系比绝对值更有参考价值。静态皮肤的增量在十几兆以内,对现在的设备来说基本可以忽略;高频动画皮肤的峰值超过一百兆,这个数量级已经接近一个中等规模应用的常驻占用。
另一件值得注意的事:空闲增量这一列。默认皮肤和静态皮肤的空闲增量接近零,说明它们在候选框隐藏时不占额外资源。两类动画皮肤则不同,低频的也有二十多兆常驻,高频的接近六十兆。这部分开销在你没打字的时候也是存在的。
内存占用高不等于卡顿:区分三种不同的卡顿
这是本篇最需要说清楚的一节。「皮肤占内存所以卡」这个推论,中间少了好几步。
实际使用中能感觉到的「卡」至少有三种,它们的原因完全不同:
- 候选框弹出延迟——按下第一个键到候选框出现之间的间隔。这一项和皮肤直接相关,动画皮肤需要准备首帧,确实会更慢一些。
- 输入延迟——连续打字时候选框更新候选词的速度。这一项主要取决于词库检索和候选排序,和皮肤关系不大。
- 系统整体卡顿——切换窗口、打开程序时的停顿。这一项通常是内存不足导致的换页,皮肤只是众多占用方之一。
皮肤开销主要影响的是第一种,而且是三种里最容易被察觉但其实影响最小的。候选框弹出延迟从几十毫秒变成一百多毫秒,人能感觉到,但对实际打字速度的影响很有限。
第二种是很多人以为的「皮肤导致打字卡」,但实际原因往往在别处。如果打字快了之后候选词更新跟不上,方向应该看向词库检索而不是皮肤。
第三种才是真正的性能问题,但它和单个皮肤的关系很弱。系统整体卡顿通常意味着可用内存已经不够,此时换皮肤省下的那几十兆,相对于缺口来说是杯水车薪。
边界:这几种卡顿换皮肤不会有改善
下面几种情况,无论换成多轻量的皮肤都不会有明显变化。判断清楚可以省下反复换皮肤的时间。
第一种是卡顿发生在打开程序或切换窗口时。这类停顿和输入法的候选框渲染无关,皮肤只在候选框显示期间参与渲染,其他时刻几乎不产生负载。换皮肤对这类卡顿没有影响。
第二种是可用内存长期处于低位。如果任务管理器里可用内存常年只有几百兆,系统会频繁把不活跃的进程换到磁盘。输入法进程被换出后,第一次唤回需要从磁盘读回,这个延迟和皮肤无关。这种情况下换皮肤只能省下几十兆,无法改变换页的整体压力。
第三种是输入法装在机械硬盘上。皮肤资源和程序文件都在同一块盘上,首次加载或换出后唤回的速度受磁盘读取速度限制。这时换皮肤减少的文件读取量有限,改成装在固态硬盘上会有更直接的改善。
第四种是集成显卡且同时有视频播放。视频解码和皮肤动画都要占用 GPU 的合成能力,两者叠加时可能出现候选框弹出变慢。这种情况下把皮肤换成静态的会有帮助,但如果同时开着多个视频窗口,改善幅度也有限。
第五种是输入延迟而非弹出延迟。如果你感觉到的慢是「打字时候选词跟不上手指」,那问题在词库检索路径上,换皮肤不会有任何变化。
这五条里,第二条和第五条最容易被误判成皮肤问题。因为它们都表现为「打字时感觉慢」,而打字时正好是皮肤在渲染的时段,两件事在时间上重叠了,因果关系却是另一回事。
什么时候该换回默认皮肤
给一个判断标准,不需要复杂测量。
如果你的设备内存是 8GB 或更少,且平时会同时运行浏览器和办公软件,那么动态皮肤的那几十到一百多兆是有意义的。这部分内存如果被省下来,能减少一些换页次数。这种情况下换回默认皮肤或静态皮肤是合理的。
如果你的设备内存是 16GB 以上,且没有明显的内存压力,那么皮肤的内存占用基本不会成为瓶颈。此时是否使用动态皮肤,应该按审美偏好来定,不需要考虑性能。真正会感觉到差异的场景是候选框弹出延迟,而这个差异在正常使用中很难察觉。
还有一种中间情况:设备内存够用,但你经常在电池供电下长时间工作。这时动态皮肤的功耗影响比内存影响更值得考虑,因为持续渲染会阻止 GPU 进入低功耗状态。如果续航是优先级,换成静态皮肤会有帮助,但幅度不会很大。
如果决定换回默认皮肤,可以在搜狗输入法下载页确认一下当前版本,覆盖安装后皮肤设置会回到默认状态,同时能排除掉皮肤包本身损坏导致的异常。需要说明的是,覆盖安装不会清除你下载过的皮肤,它们仍然在皮肤列表里,随时可以切回去。
收尾:内存开销和输入延迟是两件事
回到开头那个场景。换了一个动态皮肤,感觉打字变慢了——这个感觉有可能是真的,但它反映的是候选框弹出那一下的延迟,不是整体输入速度的下降。
把两个指标分开之后,判断就清楚了:如果你在意的是「每次候选框弹出来要等多久」,那皮肤的帧率和首帧准备时间确实有影响,值得挑一个轻量的;如果你在意的是「一分钟能打多少字」,那皮肤基本不参与,方向应该转向词库和候选排序。
前面几篇关于候选词和词库的内容讨论的都是后一个方向。这一篇补充的是前一个方向上的量化依据,两者不重叠,也不互相替代。
最后一条边界:如果测出来动态皮肤的增量只有二三十兆,而你的设备可用内存还有几个 G,那么这个开销在系统层面几乎不会产生可观测的影响。这种情况下继续为它调整设置,收益是很低的。反之,如果设备内存本身已经吃紧,那么处理顺序应该是先关掉真正的大户,再考虑皮肤这种量级的选择。
关于皮肤内存占用的常见问题
下面 5 个问题是读者反馈中最常被问到的
换回默认皮肤后内存没有降下来,为什么?
皮肤资源在切换之后不会立刻从内存里释放,通常要等输入法主程序重启才会回收。如果只是切换皮肤而没有重启,旧的皮肤资源仍然驻留着。先在任务管理器里确认输入法相关进程全部退出,再启动一次,重新测量内存占用。另一种可能是之前测到的增量来自别的进程,不是输入法本身。
动态皮肤的内存占用会随着使用时间一直涨吗?
正常情况下不会。动画帧在加载时一次性解码进内存,之后是按帧循环使用,不随使用时长持续增长。如果在长时间使用后观察到内存持续上升,更可能是候选词缓存的积累,而不是皮肤本身。判断方法是把皮肤切回默认后同样使用一段时间,如果内存仍然持续上升,那和皮肤无关。
笔记本用动态皮肤会更耗电吗?
会有影响,但幅度取决于皮肤的帧率和候选框的显示时长。动画帧需要 GPU 参与合成,持续的合成请求会阻止 GPU 进入低功耗状态。实际使用中这个差异不容易被感知,因为它只在候选框显示的那几秒内发生。如果电池续航是首要考量,把皮肤换成静态的会有帮助,但不要指望它能显著延长续航。
第三方皮肤和官方皮肤在资源占用上有区别吗?
有,而且差异可能比预期大。第三方皮肤由不同作者制作,资源打包方式和动画实现质量参差不齐。有些皮肤把整段动画做成逐帧大图,内存占用会明显高于同等视觉效果、用矢量化方式实现的皮肤。判断方法只能实测,看皮肤包的文件大小是一个粗略参考,但不是决定性指标。
8GB 内存的电脑还能用动态皮肤吗?
能,但要看同时运行的程序有多少。皮肤本身的内存增量在几十兆到一百多兆之间,单独看不算大。问题在于 8GB 设备通常在多任务时会频繁做内存换页,输入法进程被换出后再唤回需要时间,表现为候选框弹出变慢。如果平时会同时开着浏览器几十个标签和大型办公软件,换回静态皮肤是有意义的。