上一篇文章 ,我解释了像 JavaScript 这样的自动内存管理语言如何与内存一起工作,我还解释了像 C 语言这种手动内存管理如何工作。

当我们谈论 ArrayBuffersSharedArrayBuffers 的时候,为什么花了大篇幅介绍内存管理呢?
这是因为 Arraybuffer 为您提供了一种手动处理某些数据的方法,即使您使用的是具有自动内存管理功能的 JavaScript。

为什么你要这么做呢?
正如我们在上一篇文章中谈到的,自动内存管理是一把双刃剑,这对开发人员来说更容易了,但它增加了一些开销。在某些情况下,这种开销会导致性能问题。

例如,当你在 JS 中创建一个变量时,引擎必须猜测这是什么类型的变量,以及它应该如何在内存中表示,正因为引擎的这种猜测机制,JS 引擎通常会预留比它真正需要的变量多的多的空间。根据变量的不同,内存槽可能是所需的 2-8 倍,这可能会导致大量内存浪费。

此外,创建和使用 JS 对象的某些模式会使收集垃圾变得更加困难。如果您正在进行手动内存管理,你可以灵活的选择当前程序所需的内存分配和垃圾回收策略。

在大多数对性能不太敏感的场景无需担心自动内存管理导致的内存浪费,很多情况下,手动内存管理可能会更慢。

但是对于那些需要在底层工作以使代码尽可能快的时候,Arraybuffer 和 SharedArrayBuffers 给了你一种选择。

ArrayBuffer 是如何工作的呢?

使用 ArrayBuffer 就像使用 JavaScript 数组一样,有一点不同的是,当使用 ArrayBuffer 时,您不能将任何 JavaScript 类型放入其中,例如对象或字符串,你唯一能放入其中的是字节 bytes (二进制,可以用数字 0 & 1 来表示)。

再次说明一下,你实际上并没有将此字节直接添加到 ArrayBuffer 中。就其本身而言,这个 ArrayBuffer 不知道字节应该有多大,也不知道如何将不同种类的数字转换成字节。

ArrayBuffer 本身只是 0 和 1 的组合。ArrayBuffer 不知道该数组中第一个元素和第二个元素之间的区分应该在哪里。

正如上一篇文章所讲到的,为了将它分解并装进盒子,我们需要将它包装在所谓的视图中。这些数据视图可以用类型化数组添加,并且有许多不同类型的类型化数组可以使用。

例如,您可以用一个 Int8 类型的数组,将其分解为 8 位字节。

或者你可以用一个无符号的 Int16 数组,这将把它分成 16 位字节,并处理它,就像它是一个无符号整数。

您甚至可以在同一基本缓冲区上拥有多种视图。对于相同的操作,不同的视图会给你不同的结果。

例如,如果我们从这个 ArrayBuffer 上的 Int8 视图中获取元素 0 & 1,它会给我们不同于 Uint16 视图中的元素 0 的值,即使它们包含完全相同的位。

这样,ArrayBuffer 基本上就像原始内存一样。它模拟了像 C 这种直接操作内存访问的语言。

你可能疑惑为什么不直接给程序员访问内存的权限,而是添加这一抽象层?原因是直接访问内存会导致一些安全漏洞,我将在以后的文章中解释更多。

什么是共享缓冲区?

为了解释 SharedArrayBuffer,我需要讲一点关于并行和 JavaScript 运行代码的知识。

您可以通过并行运行代码,以使代码运行得更快,或者使其对用户事件的响应更快。要做到这一点,你需要拆分工作。

在一个典型的应用程序中,工作都是由一个单独的人来完成的 – 主线程。我以前谈过这个…… 主线程就像一个全栈开发人员。它负责 JavaScript、DOM 和布局。

帮助主线程从工作负载中减少任何工作都有助于性能的提升。在某些情况下,SharedArrayBuffer 可以减少主线程必须要做的工作。

但是有时减少主线程的工作量是不够的,有时你需要增援…… 来拆分工作。

在大多数编程语言中,通常拆分工作的方式是使用线程,这基本上就像让多人参与一个项目。如果你有彼此非常独立的任务,你可以把它们交给不同的线程。然后,这两个线程可以同时处理它们各自的任务。

在 JavaScript 中,这样做的方式是使用一个叫做 web worker。web worker 与您在其他语言中使用的线程略有不同,默认情况下,它们不共享内存。
02_09-500x360.png
这意味着如果你想与另一个线程共享一些数据,你必须复制它。这是用函数PostMessage 完成的 。
PostMessage 获取您放入其中的对象,将其序列化,并将其发送到其他 web worker,在那里它被反序列化并放入内存。

这是一个相当缓慢的过程。

对于某些类型的数据,如 ArrayBuffers,您可以执行所谓的传输内存。这意味着将特定的内存块移到上面,以便其他 web worker 可以访问它。

但是第一个  web worker 再也无法访问它了。

这种方式在一些场景下是没有问题的,但是在许多你想要拥有高性能并行性的场景下,你真正需要的是拥有共享内存 – SharedArrayBuffers 解决的就是这类问题

使用 SharedArrayBuffer,无论是 web worker 还是 线程(threads)都可以从同一内存块中写入数据和读取数据。
这意味着他们没有 postMessage 的通信开销和延迟。web worker 的两端都可以立即访问数据。

然而,同时从两个线程 (threads)直接访问有一些危险。它会导致 竟态条件

我会解释更多在 下一篇文章

SharedArrayBuffers 浏览器兼容性如何?

SharedArrayBuffers 将很快被所有主要浏览器支持。

在 Safari 中以及支持 (在 Safari 10.1 中),Firefox 和 Chrome 都将在 7 月/8 月发版支持。Edge 在他们的秋季 Windows 更新中支持。

即使它们在所有主流浏览器中都可用,我们也不期望应用程序开发人员直接使用它们。事实上,我们建议反对它,您应该使用可用的最高级别的抽象。

我们所期望的是,JavaScript 库开发人员将其封装成库,为您提供更简单、更安全的方法来使用 SharedArrayBuffers。

此外,一旦将 SharedArrayBuffers 内置到平台中,WebAssembly 就可以使用它们来实现对线程的支持。一旦实现到这一步,你就可以使用像 Rust 这样的语言的并发抽象,它的主要目标之一是毫不担心的使用并发。
下一篇文章 ,我们将查看工具 ( 原子 ),这些库的作者将使用它来建立这些抽象,同时避免竟态条件
02_15-500x335.png