「译」图解 ArrayBuffers 和 SharedArrayBuffers

做者:Lin Clark
译者:Cody Chan
原帖连接:A cartoon intro to ArrayBuffers and SharedArrayBuffers

这是图解 SharedArrayBuffers 系列的第二篇:web

  1. 内存管理碰撞课程segmentfault

  2. 图解 ArrayBuffers 和 SharedArrayBuffers数组

  3. 用 Atomics 避免 SharedArrayBuffers 竞争条件浏览器

上一篇文章中,我解释了 JavaScript 这类自动管理内存的语言是如何处理内存的,一样也解释了相似 C 语言这种手动管理内存的语言安全

为何这对于咱们讨论的 ArrayBuffersSharedArrayBuffers 如此重要?多线程

由于即便你使用的是 JavaScript 这种自动管理内存的语言,ArrayBuffers 也提供了一种手动处理数据的途径并发

为何你会有这样的需求呢?工具

正如上篇文章所说,这里有个权衡,自动管理内存对开发者是友好的,可是这会增长机器负担,甚至会有性能问题post

例如,JS 里建立一个变量,引擎会去猜想变量的类型以及内存里如何表示。由于有了类型猜想,JS 引擎一般会比真实须要预留更多的空间。根据变量不一样,内存分配可能会是真实需求的 2-8 倍,这致使了内存浪费性能

并且,某些建立和使用 JS 对象的场景会让垃圾回收变得很困难。若是你是手动维护的内存,能够根据实际使用需求来决定分配和释放内存的策略

不少时候,这不是什么大不了的事。大多数场景并不会对性能要求那么苛刻,反而更多地担忧管理内存的麻烦。并且通常状况下,手动管理内存可能更慢

可是对于底层须要极致优化的场景,ArrayBuffers 和 SharedArrayBuffers 为你提供了可能

ArrayBuffer 是如何工做的

ArrayBuffer 跟其它 JavaScript 数组差很少,可是不是全部 JavaScript 类型均可以放进去,好比对象、字符串。你惟一能够放进去的只有字节(能够用数字表示)

须要澄清的一点是,你事实上不是直接把这个字节到 ArrayBuffer 里就好了,ArrayBuffer 并不知道字节有多长,该用多少位去存

ArrayBuffer 仅仅是一个个 0/1 组成的串,它不知道第一个元素和第二个元素的分割点

为了提供必要的上下文信息,把 ArrayBuffer 分块,咱们须要把它包裹到视图里,这些数据的视图能够经过带类型的数组添加,已经支持不少种类型的数组了

例如,你能够用一个 Int8 类型的数组把 0/1 串分割成 8 位一组的序列

或者你能够用一个无符的 Int16 类型数组,把它分割成 16 位一组的序列,能够把它看成无符整型处理

甚至你能够在同一个基础 buffer 上同时处理多种视图,不一样视图在相同操做下会返回不一样的结果

例如,若是咱们从某个 ArrayBuffer 的 Int8 视图获得第 0 和第 1 个元素的值,在 Uint16 视图下,第 0 个元素与其有相同二进制位值,可是获得的值也会不同

这种方式下,ArrayBuffer 几乎是扮演原始内存角色了,它模拟内存的各类跟 C 语言里相似的操做

你可能纳闷了,为何不让开发者直接操纵内存而是采用这个抽象层。由于直接操做内存会有安全风险,这个之后的文章会讲

什么是 SharedArrayBuffer

为了说明白 SharedArrayBuffers,我须要稍微解释下并行运行代码和 JavaScript 的关系

为了更快运行代码或者更更快响应用户事件,你可能会让代码并行运行,为了作到这点,你须要分割工做

一个典型的应用中,全部的工做都由一个单独的主线程处理,这点我以前提到过……这个主线程就像一个全栈工程师,掌管着 JavaScript、DOM 和 视图

任何可以从主线程负载减小工做的方法都对代码运行效率有帮助,某些状况下,ArrayBuffers 能够减小大量应该由主线程作的工做

可是也有些时候减小主线程负载是远远不够的,有时你须要增援,你须要分割你的任务

大多数语言里,这种分割工做的方法可使用多线程实现,这就像不少人同时在一个项目里工做。若是你能够完美地把任务分割为多个独立的部分,你能够分给不一样的线程,而后,这些线程就同时各类独立执行这些任务

在 JavaScript 里,你能够借助 web worker 作这种事,这些 web workers 跟其它语言的线程仍是有些区别的,默认它们不能共享内存

这意味着若是你想分配你的任务给别的线程,你须要完整把任务复制过去,这能够经过 postMessage 实现

postMessage 把你传给它的任何对象都序列化,发送到其它 web worker,而后那边接收后反序列化并放进内存

这个过程是很是慢的

某些类型数据(如 ArrayBuffers)你能够经过移动内存的方式实现,这意味着把某个特定区域的内存移过去后其它 web worker 就能够直接访问了

可是,以前的 web worker 就没法访问了

对于某些场景这是实用的,可是也有不少场景对性能要求高,你只能使用共享的内存

而这就是 SharedArrayBuffers 为你提供的

有了 SharedArrayBuffer 后,多个 web worker 就能够同时读写同一块内存了

你不再须要 postMessage 伴有时延的通讯了,多个 web worker 对数据访问都没有时延了

固然,这种同时访问也有风险,会产生竞争条件

这个下一篇文章会细说

SharedArrayBuffers 支持状况

全部主流浏览器都将会支持 SharedArrayBuffers

Safari 10.1 已经支持了,Firefox 和 Chrome 也会很快支持并发布,Edge 会在他们秋季 Windows 更新的时候发布

即便全部主流浏览器都支持了,咱们也不但愿开发者直接使用它们,事实上,咱们是反对的。你应该只使用更高级的封装好的抽象层接口

咱们期盼的是 JavaScript 库开发者能够提供更简单安全的方法来使用 SharedArrayBuffers

并且,一旦 SharedArrayBuffers 内置到平台中,WebAssembly 能够经过它实现多线程,到那时候你就可使用相似 Rust 的多线程语言轻松玩转多线程了

下一篇文章咱们会介绍一个为避免竞争条件的库提供基础操做的工具(Atomics

相关文章
相关标签/搜索