【问题标题】:Is it faster to draw a series images on a Canvas with WebAssembly?使用 WebAssembly 在 Canvas 上绘制系列图像是否更快?
【发布时间】:2020-08-06 03:21:03
【问题描述】:

我想问一下使用 WebAssembly/WASM 将一系列图像绘制到画布中是否更快、更节省内存?

我问这个问题是因为我已经测试过在主 UI 和网络工作者中绘制一系列图像(动画)。对于少数画布(1-5 个画布),性能可以忍受,但是对于更多画布(如 20-25),场景会有所不同,渲染变得慢动作。

这是主界面中的代码:

const videoDecoder = new Worker("videoDecoder.js");
const canvas = document.querySelector("#canvas");
const offscreen = canvas.transferControlToOffscreen();
videoDecoder.postMessage({ action: 'init', canvas: offscreen }, [offscreen] );

这是我的网络工作者:

onmessage = async function (e) {
    const blob = e.data;
    blob.arrayBuffer().then(arrayBuffer => {
        const uint8Array = new Uint8Array(arrayBuffer);
        for(;;;) {
           const offsetIdx = ...;
           const endIdx = ...;
           const jpegArray = uint8Array.slice(offsetIdx, endIdx);
           const blob = new Blob([jpegArray], {type: "image/jpeg"});
           drawImage(blob);
        }
    }
}

async function drawImage(blob) {
    const bmp = await createImageBitmap(blob);
    context.drawImage(bmp, 0, 0);
    bmp.close();
}

这很好,它可以将图像从 web worker 渲染到浏览器,但如前所述,在 1-5 个画布上它仍然很流畅,但是随着更多画布,浏览器中的渲染变得非常慢。

所以我想问使用 WebAssembly 在 Canvas 上绘制一系列图像是否更快?或者与现在使用 web worker 方法相比,理论上不会有性能提升?

【问题讨论】:

  • webassembly 无法访问 DOM - 画布是 DOM 的一部分 - 所以,不,这是不可能的
  • @jaromanda-x 根据stackoverflow.com/questions/42806037/modify-canvas-from-wasm 和我在网上找到的其他博客文章,可以使用共享内存从 WASM “绘制”到画布。
  • 好吧,你自己试试看
  • 我关心的主要是性能方面,在我尝试为 wasm 将我的网络工作者“重写”为 C++ 之前,我想问一下是否有人体验过与单独使用 Javascript 相比,性能提升多少 - - 或者没有,所以我想提前知道是否有必要承担做 WASM 的任务。

标签: javascript html html5-canvas webassembly emscripten


【解决方案1】:

对于字节数组上的复杂计算,WebAssembly 可能更快,但主要的 JavaScript UI 线程仍将渲染 ArrayBuffercanvas。只要您通过在主 UI 线程和 WebWorker 之间使用 Transferable 接口传递对象来“重用”ArrayBuffer 实例,您就应该能够轻松地高效地更新画布。

当为多个canvas 对象呈现更新时,请考虑在requestAnimationFrame() 函数中对它们进行批处理,以最大限度地减少浏览器中像素的重绘。这应该会显着提高浏览器帧速率性能。

【讨论】:

  • 所以你的意思是,在这种情况下使用 Web Assembly 与 Web Worker 相比,性能没有显着提升?
  • 这完全取决于发生了多少计算。我总是推荐所有性能比较的 A/B 测试和测量。从理论上讲,对于更复杂的算法,WASM 应该更快。您的慢动作可能是由于主 UI 线程上的多次重绘造成的
  • 在我的例子中,所有画布都在 web worker 中“渲染”:const offscreen = canvas[i].transferControlToOffscreen(); 所以留在主 UI 线程中的代码是将这些画布传递给 web worker,当然还有网络工作者的实例化。因此,在呈现 20 个画布的接收应用程序的情况下,没有 requestAnimationFrame。一切都以字节的形式来自 websocket,并进入视频解码器工作人员。
  • 事实上这里是渲染画布的完整代码:gist.github.com/questionaut/8d01108cd6152495762b021d0e93b285
【解决方案2】:

所以我想问一下使用 WebAssembly 在 Canvas 上绘制一系列图像是否更快?

WebAssembly 可以比 JavaScript 更快,但并非总是如此。这在很大程度上取决于您正在执行的计算类型。 WebAssembly 的最大优势可能是性能是可预测的和恒定的。这篇博文可能提供了最丰富的实际性能结果:

https://hacks.mozilla.org/2018/01/oxidizing-source-maps-with-rust-and-webassembly/

或者与现在使用 web worker 方法相比,理论上不会有性能提升?

这两种方法绝不是相互排斥的!您可以在 Web Worker 中运行 WebAssembly 并使用共享内存(类似于 SharedArrayBuffer)来允许跨线程访问图像数据。这篇博文举例说明了如何使用 WebAssembly 跨多个工作线程计算算法:

https://blog.scottlogic.com/2019/07/15/multithreaded-webassembly.html

根据您的具体情况,视频解码器(通常用 C++ 编写)非常适合 WebAssembly。我建议创建多个 web worker,每个都带有一个基于 WebAssembly 的解码器。

【讨论】:

    猜你喜欢
    • 2019-09-20
    • 1970-01-01
    • 2014-11-18
    • 2013-06-19
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 2011-08-28
    相关资源
    最近更新 更多