[Flutter翻译]macOS性能比较:Flutter Desktop vs. Electron

2,220 阅读18分钟

本文由 简悦SimpRead 转码,原文地址 getstream.io

了解开发桌面应用时Flutter和JavaScript的性能差异 a......

在跨平台领域,Flutter和JavaScript是开发者和工程团队的首要选择。本文探讨了Flutter和Electron--两个让你的应用程序在桌面上运行的解决方案之间的性能差异。两者都是流行的选择,但其中的性能差异值得考虑。

这篇文章包括以下内容。

  • 支持Flutter和Electron的底层引擎和技术。
  • 重要的性能考虑因素以及它们与Flutter Desktop和Electron的关系。
  • macOS上演示Flutter Desktop和Electron应用程序的真实性能指标。

在继续阅读之前,请记住,这篇文章并不是为了推广一种解决方案而不是另一种。现代计算机和设备上的资源已经足够多,你很难发现一个制作精良的Flutter桌面应用程序与一个制作精良的Electron应用程序之间的区别。

性能仅仅是一个指标。你的团队的开发人员的生产力、经验和知识也是决定使用哪种解决方案时需要考虑的重要因素。

免责声明。本文作者是getstream.io的Flutter开发者。为了保证质量,JavaScript工程师审查了这篇文章。虽然我们尽了一切努力在这两种技术之间创建一个公正的、公平的比较,但可能存在无意的错误或不正确的测量。

这些测试所使用的所有代码都在文章中进行了链接;请随时发现错误或不一致的地方,并帮助提高结果的质量。

Electron和Flutter桌面的现状

Electron

Electron经受住了时间的考验,并被证明是将网络应用带到桌面上的一个好的解决方案。事实上,我最喜欢的(也是最常用的)一些桌面应用都在Electron上运行,比如Visual Studio Code、Figma和Slack。其他流行的应用程序包括Discord、Skype和Tusk。

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

来源。twitter.com/rishmsft/st…

这些都是大公司和大应用程序。大量的资源和工程力量被用于改善Electron、NodeJS、V8引擎、JavaScript、HTML和整个网络。看看微软在优化VSCode方面所做的工作,使其感觉像一个本地应用程序。

但是,每一个好的Electron应用程序,都有一个坏的应用程序(不要指名道姓👀)。Electron应用程序以其大的可执行文件尺寸、大量的内存/资源使用而闻名,而且在某些情况下,启动时间长,并有jank问题。

Flutter

Flutter有一个雄心勃勃的目标,即从一个代码库中瞄准各种设备。Flutter已经证明了自己是一个很好的移动(Android和iOS)开发解决方案,自其V1版本发布以来,其性能得到了稳步提高。多年来有一些性能问题,但大部分问题已经得到解决。

然而,Flutter Desktop,在撰写本文时仍处于测试阶段。目前还没有用Flutter Desktop制作的大名鼎鼎的应用程序,我们可以进行实验并与Electron直接比较。时间会证明它在生产场景中是一个多好的解决方案,但从我的个人经验来看,最初的性能结果似乎很有希望。从理论上讲,一个在移动端运行良好的Flutter应用程序在桌面上也应该运行得很好,甚至更好。

关于JavaScript V8引擎和Dart Native的简要说明

让我们来讨论一下驱动Electron和Flutter的语言和技术。

JavaScript V8引擎

Electron结合了Chromium的渲染库和Node.js。两者共享同一个JavaScript引擎,V8。直接引用V8 docs

V8是谷歌的开源高性能JavaScript和WebAssembly引擎,用C++编写。它被用于Chrome和Node.js,以及其他。

V8编译和执行JavaScript源代码,处理对象的内存分配,以及垃圾收集它不再需要的对象。V8的stop-the-world、generation、精确的垃圾收集器是V8性能的关键之一。

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

也可以使用本地模块与本地代码对接(见本文的例子)。

Dart Native

根据Dart Native docs

在开发过程中,快速的开发周期对迭代至关重要。Dart虚拟机提供了一个及时的编译器(JIT),带有增量的重新编译(实现热重载),实时指标集合(支持DevTools),以及丰富的调试支持。

当应用程序准备部署到生产中时--无论您是发布到应用程序商店还是部署到生产后端--Dart AOT编译器都可以提前编译为本地ARM或x64机器代码。经过AOT编译的应用程序在启动时具有一致的、短的启动时间。

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

关键的收获是,Dart可以以热重载的形式给你带来快速的开发者体验,并在你创建发布构建时,给目标架构带来小型编译应用程序的好处。

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

  • Dart最近增加了对健全空安全的支持,这使得Dart AOT编译器能够产生更快的机器代码(查看Dart和健全类型的性能优势了解更多信息)。
  • Dart的外国函数接口(FFI)可以让你使用现有的C库,以获得更好的可移植性,可以选择使用高度调整的C代码来完成性能关键的任务。阅读Dart 2.13了解更多关于Dart FFI的信息。

Dart正在成长为一种单独的伟大语言,但它的流行绝对可以归功于Flutter的崛起。谷歌和Flutter团队维护着Dart,这意味着该团队可以优化框架(Flutter)和语言(Dart)以帮助改善开发者的体验。

性能方面的考虑

下面的章节比较了Flutter和Electron的性能考虑。这些是可能对最终应用程序的性能产生影响的领域,或者是你的开发者经验和避免潜在性能问题所需的工作量。

FlutterElectron docs都涵盖了每个平台的性能,你应该查看这些最新的信息。

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

要考虑的一个重要因素是你编译的应用程序的可执行大小。这里Flutter是明显的赢家。Flutter编译为机器代码,这意味着Dart可以很聪明地决定在最终构建时包括哪些内容,剥离哪些内容。请看这个已关闭的PR列表,与Dart的AOT构建大小有关。这是一个不断改进和测量的领域。

另一方面,Electron应用程序在Chromium上运行。基本上,每个Electron应用程序都与它自己的浏览器捆绑在一起。这就是使Electron成为可能的神奇之处,但也是为什么你在一个简单的 "Hello, World!"应用程序中得到一个大约150mb(或更多)的可执行文件。一个类似的Flutter应用程序大约是30MB。(稍后,在我们的性能指标中,我们将看一下一些真实的数字来评估包的大小)。

忽略一个Electron应用程序的纯粹的大小,还有其他重要的因素需要考虑。

  • 在Electron中,你需要更加注意你所包含的模块,因为未使用的代码仍然会被包含在你的最终包中(见不小心包含模块一节)。
  • 在Electron中加载模块是一个令人惊讶的昂贵的操作。作为开发者,你需要注意将大模块的加载推迟到需要的时候(见过早加载和运行代码一节)。
  • 最后,建议你将所有应用程序的代码捆绑在一起,因为运行 "require "是一个昂贵的操作(有各种JavaScript捆绑器可以帮助你做到这一点,比如Webpack、Parcel和rollup.js等等)。

如果你是一个铁杆的JavaScript开发者,你可能会揉揉眼睛说,"很简单",但这些小麻烦会增加。特别是如果你是一个新的开发者。

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

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

隔离器、网络工作者和进程

Flutter和Electron应用程序都可以从Main/UI线程中卸载昂贵的工作,当你想执行有可能是UI阻塞的操作时,这一点是必要的。

  • 在Dart中,每个线程都在自己的隔离区中,有自己的内存,不同的隔离区可以沟通的唯一方式是通过端口传递数值。
  • 在Electron中,你可以使用worker threads,使线程的使用能够并行地执行JavaScript。

关于这些进程的更多信息,请参见Electron的阻断主进程和Flutter的隔离器和事件循环

虽然这两个框架都能以非阻塞的方式执行昂贵的工作,但需要注意的是,Electron默认会启动几个额外的进程,因为它正在运行Chromium。

看看这个来自**macOS活动监视器的截图吧。

活动监视器展示了MacOS上的Electron "Hello, World "应用程序。

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

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

看看Cameron Notes的优秀文章,深入了解Electron的主进程和渲染器进程 。如果你想了解更多信息,这是一个很好的开始。

另一方面,Flutter只运行一个进程,而且你可以选择生成额外的隔离程序来卸载主线程的工作。

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

关于Flutter隔离器的更多信息,请查看以下文章。

渲染

这些话题已经超出了本文的范围。但无论您是使用Flutter还是最新的JavaScript SDK,了解您的代码如何转化为屏幕上的像素是很重要的。

如果您想了解更多,这里有一些好的资源。

Flutter和Chromium都使用Skia,一个2D图形库,来处理渲染问题。

WebAssembly (Wasm)

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

来自MDN Web Docs

WebAssembly是一种可以在现代网络浏览器中运行的新型代码--它是一种类似于汇编的低级语言,具有紧凑的二进制格式,以接近原生的性能运行,为C/C++、C#和Rust等语言提供了一个编译目标,以便它们可以在网络上运行。它还被设计为与JavaScript一起运行,使两者能够一起工作。 ......使用WebAssembly的JavaScript APIs,你可以将WebAssembly模块加载到一个JavaScript应用程序中,并在两者之间共享功能。这允许你在同一个应用程序中利用WebAssembly的性能和力量以及JavaScript的表现力和灵活性,即使你不知道如何编写WebAssembly代码。

这是令人难以置信的。请参阅以下文章,了解更多关于Wasm的信息。

Flutter和Chrome DevTools

提高应用程序的性能并不是一件简单的事情,有时寻找性能错误就像在干草堆里找一根针。为此,你需要正确的工具来协助你。幸运的是,Flutter和Electron都可以使用奇妙的DevTools。

对于这个类别,Electron占了上风。Chrome DevTools是一个先进的软件,已经开发了很多年。性能分析、调试、网络监控、布局检查以及其他一切都很惊人。

Dart DevTools虽然令人惊叹,但仍然很年轻。我个人很喜欢使用它,它为你提供了调试Flutter应用程序所需的一切,但它肯定仍有改进的余地。

性能测试

现在我们已经讨论了每个平台的重要性能因素,让我们运行一些应用程序,看看Flutter Desktop和Electron如何比较。

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

  • 2.3 GHz 8核英特尔酷睿i9
  • AMD Radeon Pro 5500M 4GB
  • Intel UHD Graphics 630 1536 MB

源代码和安装说明可在Github资源库中找到。我们鼓励你自己运行这些测试,做你自己的实验。 请注意,这些只是演示。

你可以用许多不同的方式对这些例子进行优化,适用于Flutter和Electron。这只是一个开箱即用的性能比较;你可以通过最小的开发者努力得到什么。

此外,运行其中的一些性能测试导致了剖析的开销。当不对应用程序进行剖析时,现实世界的结果可能会有所不同。

你也应该期望不同的运行之间有一些差异。理想情况下,你应该多次运行这些测试并取其平均值(本文没有这样做,只选择了第一次运行)。

⚠️ 在这些例子中,**Electron Forge**被用来配置和运行Electron项目。NPM 版本7.23.0被使用。 ⚠️ Flutter 版本2.5.1在稳定通道上使用。 ⚠️ 注意Chromium限制了每秒帧数(fps)。更多信息请参见Chrome的无限帧率文章

"Hello World" App!

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

应用程序的启动时间

Flutter和Electron都能在一秒内打开并绘制到屏幕上,Flutter稍快。

"你好,世界!"应用程序,Flutter与Electron的启动时间对比

可执行文件的大小

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

Flutter

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

Electron

  • 183.9mb

活动监控

Flutter使用的内存最少(~38mb,而 ~100mb)。所有这些额外的Electron进程都会增加。

内存

"Hello, World" Flutter应用程序。内存~37.9mb

"Hello, World" Electron应用程序。内存。~99.6mb (44.9 + 26.1 + 18 + 10.6)
在这个例子中没有显示CPU、GPU和能源的影响。

剖析/每秒帧数

一个愚蠢的例子,但为了好玩而包括在内。在这里,应用程序的窗口被调整大小以触发重新渲染。

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

Flutter
每帧3ms以下,60fps。

Flutter,60fps,平均帧数在3ms以下

Electron
每帧约16-17毫秒,60帧。

Electron, 60fps, 平均帧时间设置为16-17ms

Lottie动画应用

这个例子加载了150个Lottie动画,并在同一时间为它们制作动画。

应用程序的启动时间

在这里,Electron应用比Flutter应用早半秒打开;但是,第一次内容满格的绘制是在同一时间。应该注意的是,Electron应用程序在最初的几秒钟内有非常明显的掉帧现象。

"Lottie Animations "应用程序,Flutter与Electron的启动时间

可执行大小

在这里,当使用Electron时,加载额外的模块可能会导致包的大小显著增加(与 "Hello, World "的样本相比,大了~80mb)。

Flutter应用程序在添加Lottie包和动画文件后只增加了~7mb。

Flutter

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

Electron

  • 259.1mb

活动监控

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

然而,最突出的结果是Electron版本使用了大量的内存。大致是2.2GB,而Flutter是170MB。

CPU/GPU
Flutter的CPU和GPU使用率较低。~130%(CPU)和 ~13%(GPU),而Electron的 ~215%(CPU)和41%(GPU)。

"Lottie" Flutter应用程序。CPU。~130.6%和GPU。~12.8%

"Lottie" Electron应用程序。CPU。~215% (0.2 + 101.6 + 113.4) 和GPU。~41%

内存

与Electron的~2.2GB相比,Flutter的内存使用量低得多,只有 ~170MB。

"Lottie" Flutter应用程序。内存。~168.5 MB

"Lottie" Electron应用程序。内存。~2261.3MB (2100 + 95.6 + 55.3 + 10.4)

能源影响

Flutter使用的能量较少,影响为~130,而Electron为 ~220。

"Lottie" Flutter应用程序。能量:~131.6

_"Lottie" Electron应用程序。能量:~220.7 (0.2 + 137.8 + 82.7) _

剖析/每秒的帧数

在这个例子中,Flutter和Electron之间有一个明显的运行时间差异。在启动时,Flutter立即运行,并保持稳定的40-45fps,而Electron在最初几秒钟显示出明显的帧数下降,然后保持20-25fps。

Flutter
每帧10-30ms不等,平均约为40-45fps。

"Lottie Animations "应用程序。Flutter桌面剖析.

Electron
每帧25-60ms不等,平均约为20fps-25fps。

"Lottie Animations "应用程序。Electron剖析。

高分辨率图像应用

这个例子通过网络加载了100张高分辨率的图片,将它们缓存起来,缩小到一个小尺寸,并连续旋转图片。

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

应用程序的启动时间

Flutter和Electron都在一秒内打开,Flutter应用的打开和加载图像的速度稍快。在Electron应用程序中,最初的几秒钟会有明显的帧数下降。

在 "旋转图像 "应用程序中,Flutter桌面与Electron的启动时间对比。

可执行文件的大小

Electron应用程序的大小与 "Hello, world "的例子完全相同。Flutter应用程序的大小为5mb,包括图像缓存包。

Flutter

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

Electron

  • 183.9mb

活动监控

在这个例子中,除了内存使用量,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。

"图像 "应用程序Flutter。内存 : ~73.5mb.

"图像 "应用程序Electron。内存。~173.5mb (74.2 + 54.4 + 29.1 + 15.8)

能量影响

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

"图像 "应用程序Flutter。能量影响:~12.5

"图像 "应用程序Electron。能量影响:~17 (1.1 + 8.4 + 7.5)

剖析/每秒帧数

在应用程序启动时,Electron版本有明显的帧数下降。但在最初的几秒钟后,两者都以60fps运行,没有问题。

Flutter

除了偶尔的帧数下降,它的渲染速度接近每帧1ms。一个轻松的60fps。

"图像 "应用程序Flutter。每帧1毫秒,60fps。

Electron
强制每帧16ms,60fps。

"图像 "应用程序Electron。帧数设置为16ms,60fps。然而,应用程序启动时帧数明显下降。

结论

你必须从所提供的数据中做出自己的结论🙂。如果你想把你的应用程序带到桌面上,两者都是很好的跨平台选择。

以下是对调查结果的总结。

Electron

  • 随着Electron应用程序的发展,它可能会在启动时间和首次内容的绘制时间上挣扎。然而,像VSCode和Figma这样的应用证明这一点是可以避免的。
  • Electron被证明是一个可行的解决方案;如果你能写出一个快速和高性能的网络应用程序,那么你也能在Electron上做到这一点。
  • 具有较大的安装尺寸和内存使用量。
  • 你需要更加注意你所包含的模块,它们何时被加载,以及你的应用程序是如何被打包的。
  • Wasm的集成可以极大地改善某些任务。

Flutter

  • Flutter Desktop仍处于Beta阶段,尚未在更大范围内得到验证。
  • Flutter将有更小的内存占用和安装尺寸。这些数字也可能会改善,特别是随着Dart的不断优化。
  • 瞄准多个平台很容易,只需要最小的开发人员配置。
  • Flutter的平台集成和FFI是非常好的解决方案。
  • Dart的null safety和它与Wasm的潜在整合都是Flutter的锦上添花。

就性能实验而言,如果能将两个大型的、类似的Flutter和Electron应用程序并排演示,并能够比较更多的真实场景,那就太棒了。

一旦Flutter的桌面应用开始出现,这将是一个有趣的对话。


www.deepl.com 翻译