发布于 2024 年 6 月 5 日,妙多技术

Motiff 妙多性能测评报告

郭子
郭子
产品运营

Motiff 妙多作为一款面向设计师的生产力工具,用户每周都要在此工作数十个小时。

在这数十小时的背后,是否有不断创新的 AI 功能来提升效率固然重要,但设计师每一次基础操作,诸如拖动、缩放、复制的体验也在无时无刻的影响设计师的效率。Motiff 妙多在设计每一项功能时,都很注重性能方向上的工程工作,以确保设计师在享受功能全面的编辑器的同时,还能获得流畅、稳定的使用体验。

概述

本次性能测评模拟了设计师完成日常工作时较为高频和必要的操作,并对过程中的性能表现进行记录,便于更直观地理解、衡量 Motiff 妙多的性能表现。在同一个测评环境下,我们同步使用 Figma 进行了一致操作并记录表现作为参考。

本报告中数据结果皆来源于我们于 2024 年 5 月完成的测评。与所有性能测评一样,测评结果可能会因设备、分辨率、操作系统、网络带宽甚至电脑后台同时运行程序数量的不同而有所不同。我们尽力使测评结果尽可能公平客观,但您的结果可能会有所不同。

本次测评的详细环境信息如下:

  • 设备:iMac(M1, 2021)
  • 屏幕:24-inch 4.5K Retina display, 2240 x 1260
  • 内存:16GB
  • 系统:macOS Sonoma 14.5
  • 浏览器:Chrome 125.0.6422.113 (arm64)
  • 版本:测评同时期的 Motiff 妙多国际版 Web 端与 Figma Web 端,在美国网络环境下访问
  • 基准文件:为确保基准文件内容足以涵盖设计师真实的工作需求,我们采用了 Google Material Design 的设计套件(Material 3 Design Kit)作为基准文件的基础,在此,特别鸣谢。在后文附录中,可查看本次测评所使用全部文件的详细信息。

性能测评清单

衡量性能的维度有很多。不同设计团队对于性能的关注也会受到自身工作流程、项目复杂度、协作范围等因素的影响,在参考了很多设计团队的工作流程和操作习惯后,我们认为“流畅““快速”“稳定”是三个具有广泛影响的重要方向:

  • 流畅 创作中,设计师需要频繁地对画板、图层进行各种操作,例如:画板移动缩放、图层拖动、批量复制等。保障这些频繁操作的流畅,需要顺滑的编辑体验和即时的视觉反馈,这些离不开优秀的渲染效果。
  • 快速 创意不应该被打断。诸多操作时的漫长、频繁的等待往往令人烦躁,其中文件打开与页面切换的影响最大。
  • 稳定 随着业务发展与协作人群的扩大,设计稿也会不断变大,逐渐卡顿乃至崩溃会让工作不得不停下。对大图层文件的稳定应对、更高的图层极限都是必要的。

针对不同方向上的特点,我们设定了不同的观测指标,以下本次测评采用的测评方法:

流畅:渲染效果

在测评中,我们使用同样图层内容,分别在 Motiff 妙多和 Figma 中进行了一致的缩放、移动等连续操作。对于这类连续型的操作,在直接的视觉感受外,我们使用了 FPS 与卡顿率 2 个指标更为具象衡量效果。

FPS

FPS,即为每秒内画面平均刷新帧数。FPS 是评估视觉流畅性的一個重要指标。FPS 越高,视觉反馈的延迟感越低。

  • 24fps:传统电影的标准帧率,被认为可以提供自然和舒适的观影体验。
  • 30fps:很多电视节目和视频游戏使用的帧率,相比 24fps 流畅一些,多用于动作场景中。
  • 60fps:高清体育转播和大多数高品质游戏使用的帧率,对于动态快速的画面,60fps 可以提供非常流畅和真实的视觉体验。

实践中,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 稳定性的控制优化,在卡顿率表现的进一步稳定控制上,我们也会持续进行优化。

在“流畅”方向的测评表现,总结来看:

  • 在缩放和移动画布操作上,Motiff 妙多和 Figma 旗鼓相当。Figma 在保持稳定上做了更多,Motiff 妙多会再接再厉。
  • 在拖动图层上,Figma 会出现明显的“掉帧”和卡顿,Motiff 妙多在视觉上更为流畅,图层移动更“跟手”。
  • 在 Option 复制大批量图层表现上,Motiff 妙多不仅复制的速度比 Figma 更快,顺滑感受也更为明显。

对于指标采集的方法,我们采用了浏览器插件的方式,可以达成已相同的采样标准,同时对 Figma 和 Motiff 妙多进行 FPS 及卡顿率的指标采集。插件当前在 Chrome 中可用,我们在下文附录中也提供了插件及指标采集的配置代码供参考,在本地配置后即可创作同时实时查看到相关性能指标表现。

速度:打开和切换耗时

为了确保涵盖到大多数设计文件的真实状态。我们使用 3 个不同大小的设计文件进行了此项测评:

  • 总图层 6 万,单页 3 万图层
  • 总图层 12 万,单页 6 万图层
  • 总图层 36 万,单页 18 万图层

排除网络抖动产生的干扰,我们对每个文件都进行了 5 次测试,以平均值作为最终的测评结果。

文件加载速度

随着文件图层数量的增加,文件打开速度也会变慢;但整体上看,Motiff 妙多在 3 个大小的文件上都取到了更快的速度表现;在针对大文件的时延控制上,也具有较好的表现。以下为 5 次测评的详细数据:

页面切换速度

在页面切换上,得益于对轻量存储及资源加载的策略优化,Motiff 妙多几乎可以实现无感切页,即使是单页 18 万的 36 万图层大文件,页面切换也能在 0.8s 完成。

以下为 5 次测评的详细数据:

我们也使用浏览器的开发者工具对页面切换的前端加载耗时进行了详细记录,在附录3中可查看。

在“速度”方向的测评表现,总结来看:

  • 在文件打开耗时上,随着文件图层数量的增长,耗时都会变长,但 Motiff 妙多在整体耗时和时长增加上都表现优秀,速度更快。
  • 在页面切换耗时上,Motiff 妙多表现较为突出,36 万的大图层文件也能在 1 秒内完成切页,相比较下,Figma 需要等待较久。

稳定:图层极限

我们在和许多使用 Figma 的朋友交流后发现,随着在 Figma 一个文件下设计内容不断增加,使用感上会越来越“卡”,也会触发内存不足的提醒。

虽然单一页面能够支持的图层“上限”是一定存在的,但 Motiff 妙多期望提高这个“上限”,在更大图层数量的页面里,依然能够支持流畅的编辑体验。

以下测评即为我们在优化的各方面性能后,和 Figma 进行的一次“极限耐力交锋”。

可以看到:

  • Motiff 妙多在最终达成 108 万图层后,依然可以流畅的完成一系列的编辑操作。
  • Figma 的最终表现停留在了 48 万图层,进行后续操作时,进入了 recovery mode 。

稳定可靠的性能表现是一个综合的结果。Motiff 妙多的百万图层表现,既得益于在流畅方向上针对自研渲染引擎的机制优化;也得益于在速度方向上对于轻量存储及资源加载的策略优化,这一点在进行大批量图层复制的速度表现上亦有所体现;更为基础的,是得益于底层的高性能架构设计,在运用计算资源时,精准调度、高效分配。在此基础上,我们会继续保持投入,不断探索提高“上限”。

附录

测试基准文件

点击链接即可下载: [1]流畅-6w图层基准文件

[2-1]速度-6w图层基准文件

[2-2]速度-12w图层基准文件

[2-3]速度-36w图层基准文件

[3]稳定性-12w图层基准文件

渲染性能监测工具

  1. 1.安装并使用浏览器插件:Tampermonkey
  2. 2.配置监测代码
    // ==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();
      });
    })();
    
  3. 3.刷新页面查看实时指标:完成配置后,刷新网页,在页面顶部即可实时查看对应页面的 FPS 及卡顿率指标。

页面切换前端耗时截图记录

点击下载 页面切换前端耗时截图记录