如何在使用 WebAssembly 进行大量计算时保持反应式 UI?

Posted

技术标签:

【中文标题】如何在使用 WebAssembly 进行大量计算时保持反应式 UI?【英文标题】:How to keep a reactive UI while doing heavy computation with WebAssembly? 【发布时间】:2020-08-14 13:17:54 【问题描述】:

我使用 C++ 库在加载时进行大量图像处理,使用 emscripten 编译并嵌入到 Angular 应用程序中。 此代码将 UI 冻结几秒钟,这对用户来说绝不是好事。

我猜这里有两个选项

将繁重的计算拆分为多个异步调用 使用threads (WebWorkers)

虽然我不确定每种方法的可行性,具体取决于计算代码。

各自的优点/不便之处是什么?使用 JS/WASM 处理繁重计算的常用方法是什么?

【问题讨论】:

【参考方案1】:

我都做了。

只是 异步 仍将在主线程上,因此它对您的情况没有任何好处。但是,如果您可以将处理分成更小的块并随着时间的推移将它们提供给requestIdleCallback,它会非常有效。这样做的缺点是您无法真正控制它何时(如果有的话)完成。根据输出的重要性,它可能不是最适合您的。好处是您确实可以访问所有 API,而不仅仅是 Web 工作者可用的 API。这是example of implementation 的“拆分成更小的块并将其提供给 requestIdleCallback”和here's how you use it。

使用网络工作线程有一个很大的好处是可以完全释放主线程,这可以让你更快地得到结果。缺点是你只能访问 web worker API,你必须找到一种方法将你的输出传回主线程(如果有必要),它仍然会占用大量 CPU(这意味着当主线程线程在技术上是“免费的”,它可能仍然会变慢)。如果您可以将任务拆分成更小的块,您甚至可以生成多个线程并获得更流畅/更快的用户体验。

主线程

可能会冻结页面(或者需要很长时间/永远不会完成) 让您可以访问所有 API 更容易开始,稍微复杂一点,您会进入闲置直到紧急的设计 将数据传入/传出不会成为瓶颈

网络工作者

释放主线程(但可能仍会减慢整个客户端的速度) 并非所有 API 都可用 一旦掌握了与主线程通信的句柄,就非常简单了 如果您尝试派生更多的工人并行工作,就会变得复杂 如果您的输出不能直接构造为 SharedArray,则传递大量数据可能是一个问题/瓶颈

【讨论】:

谢谢!但是,您是在回答我假设的 JS 吗? WebAssembly 可能对此有一些限制?就像在 C++ 中使用 requestIdleCallback 一样 @ymoreau 我只知道用于 WebAssembly 的 Rust,但我知道你至少可以从 JS 回调中调用一个 wasm 函数,这样你就可以使用 requestIdleCallback 绑定你的处理块。 这是一个例子,我正在做类似的事情,但对于requestAnimationFrame:github.com/Sheraff/boids/blob/master/js/…

以上是关于如何在使用 WebAssembly 进行大量计算时保持反应式 UI?的主要内容,如果未能解决你的问题,请参考以下文章

看前端如何通过WebAssembly实现播放器预览能力

如何使用 emscripten 生成独立的 WebAssembly

如何在 blazor webassembly 项目中对服务器端控制器中的用户进行身份验证?

WebAssembly是什么?

“网红” WebAssembly 与 K8s 如何实现双剑合璧?

在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?