阅读 1519

【译】macOS 上 Flutter Desktop 与 Electron 的性能对比

原文链接:getstream.io/blog/flutte… ,作者 Gordon H.

⚠️阅读本文须知:本文作者在 Flutter 和 Electron 的对比仅限于文中的 Demo 应用,除了原文作者的结论之外,我也在文末补充了自己的看法。

在跨平台框架的世界里,如今 Flutter 和 JavaScript 是开发人员和工程团队的首选,而本文主要讨论 Flutter 和 Electron 之间的性能差异,目前这两种解决方案都可以让开发者的应用在桌面端运行,两者都是如今流行的选项之一,但它们之间存在值得探讨的性能差异。

本文涵盖以下内容:

  • Flutter 和 Electron 的底层引擎和技术支持。
  • Flutter Desktop 和 Electron 的性能对比。
  • macOS上演示 Flutter Desktop 和 Electron 应用程序的真实性能指标对比。

⚠️在开始阅读本文之前,请记住本文的目的并不是要推广某一种解决方案,如今设备上资源足以让 Flutter 和 Electron 桌面应用完美运行。

所以性能只是一个指标,在决定使用哪种解决方案时,也需要考虑团队的开发人员的经验和知识储备

免责声明:本文作者是 getstream.io 的 Flutter 开发人员,文章也经过 JavaScript 工程师进行审核以保证质量,尽管已经尽一切努力在这两种技术之间进行公正的对比,但仍可能存在无意的错误或不正确的测量。

一、Electron 和 Flutter Desktop 的现状

1、Electron

如今的 Electron 经受住了时间的考验,并证明了自己是将 Web 应用程序带入到桌面端的良好解决方案

事实上我最喜欢(也是最常用)的一些桌面应用程序都是使用 Electron 上运行,例如 Visual Studio Code、Figma 和 Slack 等等,还有其他流行的应用程序包括 Discord、Skype 和 Tusk 。

微软正在将 Teams 从 Electron 迁移到Edge WebView2

来源:twitter.com/rishmsft/st…

这些都是大公司和体量比较大应用,大量资源和工程资源被投入到改进 Electron、NodeJS、V8 引擎、JavaScript、HTML 和整个 Web 体系中,比如微软对 VSCode 的优化让它感觉更像一个原生应用程序。

但是 Electron 也有一些其他问题:Electron 应用也因为可执行文件过大、大量内存和资源占用以及在某些情况下启动时间长和卡顿问题而闻名

2、Flutter

Flutter 有一个雄心勃勃的目标,即从单个代码库的设计就针对各种设备进行适配

Flutter 已经证明自己是一个很好的移动(Android 和 iOS)开发解决方案之一,自 v1 发布以来它的性能一直在稳步提升,虽然多年来一直存在一些问题,但其中大部分已得到解决。

Flutter 2.5 的性能优化:medium.com/flutter/wha…

但是直到在撰写本文时,Flutter Desktop 仍处于测试阶段,所以没有使用 Flutter Desktop 开发的大型应用可供试验并直接与 Electron 进行比较。

时间会证明它在生产场景中的解决方案好不好用,但从我个人的经验来看,最初的性能结果似乎还不错,理论上,在移动设备上运行良好的 Flutter 应用程序,在桌面上应该也运行得同样好,甚至更好。

二、JavaScript V8 引擎和 Dart Native 的速记

本节讨论 Electron 和 Flutter 的语言和技术。

1、JavaScript V8 引擎

Electron 结合了 Chromium 的渲染库和 Node.js,两者共享有 JavaScript 引擎 V8:

V8 是 Google 开源的高性能 JavaScript 和 WebAssembly 引擎,用 C++ 编写,它可以用于 Chrome 和 Node.js 等。

V8 编译并执行 JavaScript 源代码,处理对象的内存分配,并自动垃圾回收不再需要的对象,V8 的 stop-the-world、分代、准确的垃圾收集器是 V8 性能的关键之一。

V8 引擎在不断地改进,虽然 JavaScript 通常被认为是一种解释型语言,但现代 JavaScript 引擎不再只是解释 JavaScript,它们对 JavaScript 进行编译,V8 在内部使用即时编译(JIT)编译 JavaScript以加快执行速度。

当然也可以使用 native modules :www.electronjs.org/docs/tutori…

2、Dart Native

根据 Dart Native 文档

在开发过程中,快速迭代对于开发人员而言至关重要,Dart VM 提供即时编译器 (JIT),具有增量编译(启用热重载)、实时指标集合(支持DevTools)和丰富的调试支持。

当应用程序准备好部署到生产环境时——无论是发布到应用程序商店还是部署到生产后端——​​Dart AOT 编译器都可以提前编译为原生 ARM 或 x64 机器代码, AOT 编译的应用程序可以实现更快的启动。

AOT 编译的代码在高效的 Dart runtime 上运行,该运行时执行健全的 Dart 类型系统并使用快速为对象分配内存和垃圾收集

关键在于,Dart 可以通过热重载的形式为开发者提供快速的开发体验,并在创建发布版本时为目标架构提供一个更小的应用程序。

以下是一些其他值得注意的 Dart 功能和更新:

  • Dart 添加了对空安全的支持,这使 Dart AOT 编译器能够生成更快的机器代码(medium.com/dartlang/da…
  • Dart 的外部函数接口(FFI) 允许开发者使用现有的 C 库以获得更好的可移植性,并且可以选择使用高度优化的 C 代码来执行更好的性能任务,有关 Dart FFI 的更多信息请阅读(medium.com/dartlang/an…

Dart 正在成长为一种相对孤立的语言,但它的流行绝对可以归功于 Flutter 的兴起,而 Google 和 Flutter 团队维护 Dart,这意味着团队可以同时优化框架(Flutter)和语言(Dart)以帮助改善开发者体验。

三、性能注意事项

以下部分主要比较了 Flutter 和 Electron 之间的关于性能的注意事项,这些领域可能会影响最终应用的性能和工作量。

1、应用程序大小、依赖和模块

需要考虑的一个重要因素是已编译应用的可执行文件大小。

在这里 Flutter 明显是赢家,因为 Flutter 编译成机器码,这意味着 Dart 可以更智能地知道要包含什么代码以及最终可以在构建中删除什么。

请参阅此与 Dart 的 AOT 构建大小相关的已关闭 PR 列表,这是一个不断改进和衡量的领域。(github.com/dart-lang/s…

另一方面 Electron 应用在 Chromium 上运行,基本上每个 Electron 应用程序都是和自己的浏览器内核捆绑在一起,这就是使 Electron 变大的原因,也是为什么开发者可以通过简单的“Hello, World!”获得大约 150mb(或更多)的可执行文件的原因,而一个类似的 Flutter 应用程序大约为 30mb。(稍后在性能指标中,我们将查看一些实数来评估包大小。)

忽略 Electron 应用程序的大小规模,还有其他重要因素需要考虑:

  • 使用 Electron 需要更加注意需要代码里包含哪些模块,因为未使用的代码仍会将它们包含在最终包中(请参阅 www.electronjs.org/docs/tutori…
  • 在 Electron 中加载模块是一项非常昂贵的操作,作为开发人员需要注意将大模块的加载推迟到仅在需要时才加载(www.electronjs.org/docs/tutori…) 。
  • 最后,建议所有代码打包到一个包中,因为运行 require 是一项昂贵的操作(这里有各种 JavaScript 打包程序可以帮助完成此操作,例如 Webpack、Parcel 和 rollup.js 等) (www.electronjs.org/docs/tutori…).

如果你是一名铁杆的 JavaScript 开发人员,您可能会翻白眼说这很“简单”,但这些小麻烦会累积起来的话,特别是如果你是一个新的开发人员,就会显得相对吃力。

另一方面,Dart 在剥离不需要的代码和依赖方面做得很好,如果开发者开始一个新的 Flutter 项目,那么在 macOS(以及所有其他平台)上运行的构建已经设置好。

所以 Flutter 需要的配置少得多,当应用程序构建到目标平台时,将立即受益于 Dart 提供的所有 AOT 编译优势。

个人觉得此处不大公允,因为就环境配置和首次运行成本上来看,国内 Flutter 开发者因为某些不可抗因素,对于新人而言其实不比 Electron 方便多少。

2、Isolates, Web Workers, and Processes

Flutter 和 Electron 应用都可以在主线之外执行耗时的工作任务,这对比避免 UI 阻塞的必要的:

  • 在 Dart 中,每个线程都拥有自己的 Isolates 和自己的内存,不同隔离区之间唯一可以通信的方式是通过端口传递值。

  • 在 Electron 中可以使用Worker Threads 来支持使用线程并行执行 JavaScript。

有关这些进程的更多信息,请参阅 www.electronjs.org/docs/tutori…medium.com/dartlang/da…

虽然这两个框架都可以以非阻塞方式执行耗时的工作,但重要的是要注意 Electron 默认情况下会启动几个额外的进程,因为它运行的是 Chromium

看看来自macOS Activity Monitor 的截图:

活动监视器展示了 macOS 上的 Electron“Hello, World”应用程序。

你可能会注意到三个额外的辅助进程(Helper、GPU、Renderer),每个进程都占用一些内存和处理能力:

  • 主进程创建管理BrowserWindow实例和各种应用程序事件。
  • 渲染过程运行应用(网页)的用户界面,它是webContents 的一个实例。

查看 Cameron Notes 的优秀文章,《深入研究 Electron 的主进程和渲染器进程》了解更多信息:cameronnokes.com/blog/deep-d…

另一方面 Flutter 只运行一个进程,你可以选择自主产生额外的 Isolates 来分担主线程的工作。

活动监视器展示了 macOS 上的 Electron“Hello, World”应用程序。

有关 Flutter Isolates 的更多信息,请查看以下文章:

3、渲染

这个主题超出了本文的范围,但无论使用 Flutter 还是最新的 JavaScript SDK,了解代码如何绘制到屏幕上也是非常重要的。

如果想了解更多信息,这里有一些很好的资源:

Flutter 和 Chromium 都使用 2D 图形库Skia来处理渲染。

4、WebAssembly (Wasm)

这是一种新的二进制机器代码格式,专为浏览器而设计,编译为 WebAssembly 的应用程序可以与 JavaScript 一起运行并且不会影响性能,而 Flutter 和 Electron 应用程序都可以(并且确实)受益于这项技术。

来自MDN 网络文档

WebAssembly 是一种可以在现代 Web 浏览器中运行的新型代码:它是一种类似低级汇编语言,具有紧凑的二进制代码格式,以接近本机的性能运行,并提供 C/C++、C# 和 Rust 等语言带有编译目标,以便它们可以在 Web 上运行,它还被设计为可以与 JavaScript 一起运行。

...使用 WebAssembly JavaScript API,开发者可以将 WebAssembly 模块加载到 JavaScript 应用程序中并在两者之间共享功能。

请参阅以下文章以了解有关 Wasm 的更多信息:

5、Flutter 和 Chrome 开发者工具

提高应用程序的性能并不是一项简单的任务,有时发现性能错误可能会像是大海捞针,为此开发者就需要合适的工具来帮助调试,幸运的是 Flutter 和 Electron 都有出色的 DevTools。

而对于这个类别 Electron 显然是占了上风Chrome DevTools是一款已经开发多年的高级软件,性能分析、调试、网络监控、布局检查和其他一切都非常出色。

Dart DevTools 虽然很棒,但还是太年轻了,,仍然有改进的空间。

四、性能测试

现在我们已经讨论了每个平台上关于影响性能测试的重要因素,接下来让我们运行一些应用程序,看看 Flutter Desktop 和 Electron 的比较。

这些测试仅在macOS Big Sur v11.5.2MacBook Pro(16 英寸,2019 年)上进行,规格如下:

  • 2.3 GHz 8 核英特尔酷睿 i9
  • AMD Radeon Pro 5500M 4 GB
  • 英特尔高清显卡 630 1536 MB

Github (github.com/HayesGordon…) 中提供了源代码和安装说明,这里鼓励开发者运行这些测试并进行自己的实验。

请注意,这些只是演示, 开发者可以通过不同方式为 Flutter 和 Electron 优化这些示例,这是一个开箱即用的性能比较。

此外,运行其中一些性能测试会导致新的分析开销,如果不分析应用程序时,实际结果可能会略有所不同。

理想情况下,应该多次运行这些测试并取平均值(当然这里本文没有这样做;只选择了第一次运行)。

⚠️ 在这些示例中,Electron Forge 用于配置和运行 Electron 项目,使用了 NPM版本 7.23.0
⚠️ Flutter 2.5.1 版本用于稳定版本。
⚠️ 请注意,Chromium 限制每秒帧数 (fps),有关更多信息请参阅 Chrome 的无限帧速率文章 dev.to/uwutrinket/…

1、“Hello World” App!

一个显示“Hello, World!”的简单应用程序。

1.1、应用启动时间

Flutter 和 Electron 都在一秒钟内打开并绘制到屏幕上,Flutter 稍快一些,视频地址:youtu.be/GU99UB9IFfc

1.2、可执行文件大小

在这里我们可以看到,即使是一个简单的应用程序,一个 Electron 应用程序也会占用大量空间!

Flutter

  • 部署目标 10.11 及以上:37.3mb
  • 部署目标 11.0 及以上:22.7mb

Electron

  • 183.9mb

1.3、内存

Flutter 使用最少的内存~38mb,所有 Electron 进程加起来 ~100mb。

1.4、分析 / 每秒帧数

一个愚蠢但是有趣的例子,此处是调整应用程序窗口的大小以触发重新渲染。

正如预期的那样,Flutter 和 Electron 都能以 60fps 的速度舒适地运行,但请注意 Chromium 限制了 fps 的数量,这就是为什么 Electron 每帧的时间总是在 16ms 左右。

2、Lottie 动画应用程序

此示例加载 150 个 Lottie 动画并同时为它们设置动画。

2.1、应用启动时间

在这里 Electron 应用比 Flutter 应用提前半秒打开,然而出现第一个完整的绘画内容是在同一时间,应该注意的是 Electron 应用程序在前几秒钟有非常明显的掉帧。youtu.be/sqVtz4t8EkM

2.2、可执行文件大小

Electron 因为加载了额外的模块可能会导致包大小显着增加(与“Hello, World”示例相比,大约大 80mb)。

添加 Lottie 包和动画文件后,Flutter 应用程序仅增加了约 7mb。

Flutter

  • 部署目标 10.11 及以上:40.7mb
  • 部署目标 11.0 及以上:29.1mb

80mb

  • 259.1mb

2.3、活动监视器

Flutter 在所有类别中使用更少的资源:CPU、GPU、内存和能源。

然而更突出的结果是 Electron 版本中使用的大量内存,与 Flutter 约 170mb 相比,大约为 2.2 GB

CPU/GPU\

  • Flutter 的 CPU 和 GPU 使用率较低:约 130% (CPU) 和约 13% (GPU)
  • 相比之下 Electron 的约 215% (CPU) 和 41% (GPU)。

2.4、能源影响

Flutter 使用更少的能耗,与 Electron 的 ~220 相比,其影响为 ~130。

2.5、分析/每秒帧数

在这个例子中,Flutter 和 Electron 之间存在明显的运行时差异。

在启动时,Flutter 立即运行,并保持稳定的 40-45fps,而 Electron 在前几秒表现出明显的掉帧,然后保持 20-25fps。 youtu.be/biaGyZEINNM

  • Flutter 每帧 10-30ms 不等,平均约为 40-45fps。
  • Electron 范围为每帧 25-60ms,平均约为 20fps-25fps。

3、高分辨率图像应用程序

这个例子通过网络加载 100 张高分辨率图像,缓存它们,将它们缩小到一个小尺寸,并不断旋转图像。

请注意,在 Flutter 上 cached_network_image包用于在运行之间缓存图像。

3.1、应用启动时间

Flutter 和 Electron 都在一秒钟内打开,而 Flutter 应用程序打开和加载图像的速度稍快一些,在前几秒钟,Electron 应用程序中有明显的掉帧。youtu.be/mZa8MDTk2_c

3.2、可执行文件大小

Electron 应用程序的大小与“Hello, world”示例完全相同,Flutter 应用程序在包含图像缓存包后大 5mb。

  • Flutter 部署目标 11.0 及以上:27.7mb

  • Electron 183.9mb

3.3、活动监视器

这个例子中的结果更接近,除了内存使用量,其中 Flutter 使用的内存减少了大约 100mb。

CPU/GPU

Flutter 和 Electron 区别不大,Flutter 使用的资源略少。

  • Flutter CPU:~13.6% 和 GPU:~2.97%

  • Electron CPU:~17.5% (1.1 + 7.7 + 8.7) 和 GPU ~2.8%

内存

Flutter 使用大约比 Electron 少 100mb。

3.4、能源影响

Flutter 和 Electron 之间差别不大,能耗影响在 12-17% 之间。

3.5、分析/每秒帧数

在应用程序启动时,Electron 版本中有明显的掉帧,但是在最初的几秒钟之后,两者都以 60fps 的速度运行而没有问题。 youtu.be/e_1ZuICXmXA

  • Flutter 除了偶尔的丢帧外,它每帧渲染时间接近 1 毫秒,60fps。

  • Electron 强制每帧 16ms,60fps。

五、结论

调查结果摘要如下:

Electron

  • 随着 Electron 应用程序的增长,它可能会在启动时间和首次内容绘制的时间上有所劣势,然而像 VSCode 和 Figma 这样的应用程序证明这是可以避免的。
  • Electron 被证明是一种可行的解决方案,如果开发者可以快速且高性能地编写 Web 应用,那么也可以在 Electron 上完成。
  • 具有较大的安装大小和内存使用量。
  • 需要更加注意包含的模块、加载时间以及应用程序的打包方式。
  • Wasm 集成可以极大地改进某些任务。

Flutter

  • Flutter Desktop 仍处于 Beta 阶段,未经大规模验证。
  • Flutter 将具有更小的内存占用和安装大小,这些数字以后还可能会有所改善,尤其是随着 Dart 不断优化。
  • 只需最少的开发人员配置即可轻松定位多个平台。
  • Flutter 的平台集成和 FFI 是极好的解决方案。
  • Dart 的零安全性及其与 Wasm 的潜在集成都为 Flutter 锦上添花。

对于性能实验,如果能够并排演示两个类似的大型 Flutter 和 Electron 应用程序,并且能够比较更多真实场景,那将有更具意义的对比效果。

⚠️个人总结:Flutter 从对比数据上看好像是比 Electron 更有优势,但存在一些关键的现实因素,其中最主要的就是桌面端如今应用的开发和维护相对频次较低,所以除非有新的应用项目,否则旧桌面应用迁徙到 Flutter 可能性很低。
其次 Dart 生态不如 JS 有优势,而桌面应用的生态支持现已经相对较低,所以 Dart 很难去和 Electron 竞争,这也是 Flutter Desktop 如今还在 Beta 的原因之一。
目前 Web 开发人员用使用 Electron 开发桌面成本低于 Flutter 开发人员开发桌面应用,这也是致命伤。

对于 Flutter Desktop 比较有优势的,就是可以通过 FFI 接入各种已经存在的 C 语言编写的历史代码

文章分类
Android