Motiff 妙多作为一款面向设计师的生产力工具,用户每周都要在此工作数十个小时。
在这数十小时的背后,是否有不断创新的 AI 功能来提升效率固然重要,但设计师每一次基础操作,诸如拖动、缩放、复制的体验也在无时无刻的影响设计师的效率。Motiff 妙多在设计每一项功能时,都很注重性能方向上的工程工作,以确保设计师在享受功能全面的编辑器的同时,还能获得流畅、稳定的使用体验。
本次性能测评模拟了设计师完成日常工作时较为高频和必要的操作,并对过程中的性能表现进行记录,便于更直观地理解、衡量 Motiff 妙多的性能表现。在同一个测评环境下,我们同步使用 Figma 进行了一致操作并记录表现作为参考。
本报告中数据结果皆来源于我们于 2024 年 5 月完成的测评。与所有性能测评一样,测评结果可能会因设备、分辨率、操作系统、网络带宽甚至电脑后台同时运行程序数量的不同而有所不同。我们尽力使测评结果尽可能公平客观,但您的结果可能会有所不同。
本次测评的详细环境信息如下:
衡量性能的维度有很多。不同设计团队对于性能的关注也会受到自身工作流程、项目复杂度、协作范围等因素的影响,在参考了很多设计团队的工作流程和操作习惯后,我们认为“流畅““快速”“稳定”是三个具有广泛影响的重要方向:
针对不同方向上的特点,我们设定了不同的观测指标,以下本次测评采用的测评方法:
在测评中,我们使用同样图层内容,分别在 Motiff 妙多和 Figma 中进行了一致的缩放、移动等连续操作。对于这类连续型的操作,在直接的视觉感受外,我们使用了 FPS 与卡顿率 2 个指标更为具象衡量效果。
FPS,即为每秒内画面平均刷新帧数。FPS 是评估视觉流畅性的一個重要指标。FPS 越高,视觉反馈的延迟感越低。
实践中,FPS 的瞬时表现会受到设备本身显卡性能、屏幕分辨率及操作图层复杂度等多因素影响而波动。结合不同的需求场景特点,我们认为: FPS 保持在 50 以上,可保障优秀的使用感受与体验,而低于 20 则会有明显卡顿、延迟的糟糕感受。
下图即为我们在本次测评中采样得到的 FPS 表现:
可以看到,在缩放/移动画布时,Figma 的 FPS 表现几乎稳定在 60 的理想水平。Motiff 妙多虽然在某些时刻出现了波动,但整体视觉感受上保持了流畅,依然需要学习 Figma 的稳定表现。在拖动和批量复制上,得益于自研渲染引擎的机制优化,在这种存在较大计算量的场景下,Motiff 妙多在大部分时刻依然保持在 50 以上的目标区间内。
在 FPS 表现优秀的前提下,如果画面帧的刷新速度不均匀、时快时慢,视觉上依然会有不流畅的感受,因此,我们引入卡顿率作为评估画面帧刷新速度是否均匀的指标。当任一帧的单帧耗时大于前三帧平均耗时的 2 倍,则会被记录为卡顿;同步会记录对应的卡顿时长,在单位时间内卡顿时长占总时长的比例,计算为卡顿率,卡顿率越低视觉反馈的顺畅与平滑感越好。
可以看到,即使左边的 FPS 达到 60 的理想水平,但在卡顿率达到 20% 的状态下,流畅度依然不如右边 FPS 只有 20 的状态。同样,卡顿率也会受多种因素影响而波动。
实践中我们认为:卡顿率的目标区间应该在 20% 以下,超出时会造成不流畅的视觉感受。
下图即为我们在本次测评中采样得到的卡顿率表现:
对照可以看到,在 FPS 指标出现波动时,往往会伴随卡顿率变大。视觉感受来看,当 FPS 稳定表现时,卡顿率指标的微小波动,不会对操作的流畅感产生影响;同样得益于对自研渲染引擎的优化,Motiff 妙多的卡顿率表现当前能够保持在 20% 以下的目标区间内。跟随着 FPS 稳定性的控制优化,在卡顿率表现的进一步稳定控制上,我们也会持续进行优化。
在“流畅”方向的测评表现,总结来看:
对于指标采集的方法,我们采用了浏览器插件的方式,可以达成已相同的采样标准,同时对 Figma 和 Motiff 妙多进行 FPS 及卡顿率的指标采集。插件当前在 Chrome 中可用,我们在下文附录中也提供了插件及指标采集的配置代码供参考,在本地配置后即可创作同时实时查看到相关性能指标表现。
为了确保涵盖到大多数设计文件的真实状态。我们使用 3 个不同大小的设计文件进行了此项测评:
排除网络抖动产生的干扰,我们对每个文件都进行了 5 次测试,以平均值作为最终的测评结果。
随着文件图层数量的增加,文件打开速度也会变慢;但整体上看,Motiff 妙多在 3 个大小的文件上都取到了更快的速度表现;在针对大文件的时延控制上,也具有较好的表现。以下为 5 次测评的详细数据:
在页面切换上,得益于对轻量存储及资源加载的策略优化,Motiff 妙多几乎可以实现无感切页,即使是单页 18 万的 36 万图层大文件,页面切换也能在 0.8s 完成。
以下为 5 次测评的详细数据:
我们也使用浏览器的开发者工具对页面切换的前端加载耗时进行了详细记录,在附录3中可查看。
在“速度”方向的测评表现,总结来看:
我们在和许多使用 Figma 的朋友交流后发现,随着在 Figma 一个文件下设计内容不断增加,使用感上会越来越“卡”,也会触发内存不足的提醒。
虽然单一页面能够支持的图层“上限”是一定存在的,但 Motiff 妙多期望提高这个“上限”,在更大图层数量的页面里,依然能够支持流畅的编辑体验。
以下测评即为我们在优化的各方面性能后,和 Figma 进行的一次“极限耐力交锋”。
可以看到:
稳定可靠的性能表现是一个综合的结果。Motiff 妙多的百万图层表现,既得益于在流畅方向上针对自研渲染引擎的机制优化;也得益于在速度方向上对于轻量存储及资源加载的策略优化,这一点在进行大批量图层复制的速度表现上亦有所体现;更为基础的,是得益于底层的高性能架构设计,在运用计算资源时,精准调度、高效分配。在此基础上,我们会继续保持投入,不断探索提高“上限”。
点击链接即可下载: [1]流畅-6w图层基准文件
[2-1]速度-6w图层基准文件
[2-2]速度-12w图层基准文件
[2-3]速度-36w图层基准文件
// ==UserScript==
// @name Smooth performance
// @namespace <http://tampermonkey.net/>
// @version 2024-05-20
// @description FPS & Jank rate
// @author Motiff
// @match <https://www.figma.com/*>
// @match <https://motiff.com/*>
// @match https://motiff.cn/*
// @icon <https://www.google.com/s2/favicons?sz=64&domain=motiff.com>
// @grant none
// ==/UserScript==
(function () {
var stutterPanel = document.createElement("div");
stutterPanel.setAttribute("id", "monitor");
stutterPanel.style.position = "fixed";
stutterPanel.style.left = "65%";
stutterPanel.style.top = "15px";
stutterPanel.style.color = "red";
stutterPanel.style.zIndex = 10000;
document.body.append(stutterPanel);
let lastRenderTime = performance.now();
let lastStatisticsTime = lastRenderTime;
let renderCount = 0;
let maxRenderDuration = 0;
let renderDurations = [];
let stutterDurationSum = 0;
function render() {
const now = performance.now();
const renderDuration = now - lastRenderTime;
renderDurations.push(renderDuration);
if (renderDurations.length > 3) renderDurations.shift();
const avgRenderDuration =
renderDurations.reduce((a, b) => a + b, 0) / renderDurations.length;
if (renderDuration > 2 * avgRenderDuration) {
stutterDurationSum += renderDuration;
}
maxRenderDuration = Math.max(maxRenderDuration, renderDuration);
lastRenderTime = now;
renderCount += 1;
const statisticsDuration = now - lastStatisticsTime;
if (statisticsDuration > 1000) {
const fps = (renderCount / statisticsDuration) * 1000;
const date = new Date()
const currentTime = date.getHours() + ":" + date.getMinutes().toString().padStart(2, '0') + ":" + date.getSeconds().toString().padStart(2, '0');
const logTime = (date.getMonth() + 1).toString().padStart(2, '0') + "-" + date.getDate().toString().padStart(2, '0') + "-" + date.getFullYear() + " " + date.getHours() + ":" + date.getMinutes().toString().padStart(2, '0') + ":" + date.getSeconds().toString().padStart(2, '0') + "." + date.getMilliseconds();
console.log(`${logTime} |${Math.floor(fps * 100) / 100} |${Math.floor(stutterDurationSum / statisticsDuration * 10000) / 100} |${Math.floor(maxRenderDuration * 100) / 100} `);
stutterPanel.innerHTML = currentTime
+ " | FPS: " + fps.toFixed(2)
+ " | Jank rate: " + Math.floor(stutterDurationSum / statisticsDuration * 10000) / 100 + "%"
lastStatisticsTime = now;
renderCount = 0;
maxRenderDuration = 0;
stutterDurationSum = 0;
}
requestAnimationFrame(() => {
render();
});
}
requestAnimationFrame(() => {
render();
});
})();
点击下载 页面切换前端耗时截图记录