将一个大的 blob 复制给工人是否昂贵?

bit*_*low 6 javascript blob web-worker arraybuffer

使用 Fetch API,我能够为大量二进制数据资产(比如超过 500 MB)发出网络请求,然后将其转换Response为 aBlobArrayBuffer.

之后,我可以worker.postMessage让标准结构化克隆算法将其复制Blob到 Web Worker 或将其ArrayBuffer转移到工作器上下文(使主线程不再有效)。

起初,似乎将数据作为ArrayBuffera获取会更可取,因为 aBlob不可传输,因此需要复制。但是,blob 是不可变的,因此,浏览器似乎没有将其存储在与页面关联的 JS 堆中,而是存储在专用的 blob 存储空间中,因此,最终被复制到工作线程上下文的内容只是一个参考。

我准备了一个演示来尝试两种方法之间的区别:https : //blobvsab.vercel.app/。我正在使用这两种方法获取价值 656 MB 的二进制数据。

我在本地测试中观察到的一些有趣的事情是,复制 Blob 甚至比传输 更快ArrayBuffer

Blob 从主线程到工作线程的复制时间:1.828125 毫秒

ArrayBuffer 从主线程到工作线程的传输时间:3.393310546875 毫秒

这是一个强有力的指标,表明处理 Blob 实际上非常便宜。由于它们是不可变的,浏览器似乎足够聪明,可以将它们视为引用,而不是将覆盖的二进制数据链接到这些引用。

以下是我在作为 a 获取时拍摄的堆内存快照Blob

以 blob 形式获取二进制数据时堆内存检查器的屏幕截图

前两个快照是在Blob使用postMessage. 请注意,这些堆都不包含 656 MB。

后两个快照是在我使用 aFileReader实际访问底层数据之后拍摄的,并且正如预期的那样,堆增长了很多。

现在,这就是直接作为一个 fetch 发生的情况ArrayBuffer

获取二进制数据作为数组缓冲区时堆内存检查器的屏幕截图

在这里,由于二进制数据只是通过工作线程传输,主线程的堆很小,但工作堆包含全部 656 MB,即使在读取这些数据之前也是如此。

现在,环顾四周,我看到ArrayBuffer 和 Blob 之间有什么区别?提到了两种结构之间的许多潜在差异,但我还没有找到一个很好的参考资料,说明是否应该担心Blob在执行上下文之间复制与ArrayBuffer它们可转移的固有优势。然而,我的实验表明,复制Blob实际上可能更快,因此我认为更可取。

似乎取决于每个浏览器供应商如何存储和处理Blobs。我发现这个 Chromium 文档描述了所有内容Blobs都从每个渲染器进程(即选项卡上的页面)传输到浏览器进程,这样 Chrome 甚至可以Blob在需要时将其卸载到辅助内存。

有没有人对所有这些有更多的见解?如果我可以选择通过网络获取一些大型二进制数据并将其移动到 Web Worker,我应该更喜欢 aBlob还是 a ArrayBuffer

Kai*_*ido 5

不,postMessage一个 Blob一点也不贵。

Blob的克隆步骤

它们的序列化步骤,给定值并序列化,是:

  1. 将 serialized.[[SnapshotState]] 设置为值的快照状态。

  2. 将 serialized.[[ByteSequence]] 设置为值的底层字节序列。

给定序列化和值,它们的反序列化步骤是:

  1. 将值的快照状态设置为序列化。[[SnapshotState]]。

  2. 将值的底层字节序列设置为序列化。[[ByteSequence]]。

换句话说,什么都不复制,快照状态和字节序列都是通过引用传递的(即使包装的 JS 对象不是)。

但是,关于您的完整项目,我不建议在此处使用 Blob,原因有两个:

  1. 获取算法首先在内部以 ArrayBuffer 的形式获取。请求一个 Blob 在那里增加了一个额外的步骤(消耗内存)。
  2. 您可能需要从 Worker 读取该 Blob,并添加另一个步骤(这也将消耗内存,因为此处数据实际上将被复制)。