WebAssembly中最高可配4 GB内存

2020-05-15 01:38:44

多亏了Chrome和Emscripten最近的工作,你现在可以在WebAssembly应用程序中使用高达4 GB的内存。这比之前2 GB的限制要高。这看起来可能有点奇怪--毕竟,让人们使用512MB或1 GB内存不需要任何工作!--但事实证明,在从2 GB到4 GB的跃升过程中,浏览器和工具链中都发生了一些特殊的事情,我们将在本文中描述这一点。在我们进入更多细节之前,先了解一些背景知识:新的4 GB限制是使用32位指针可能获得的最大内存量,这也是WebAssembly目前支持的,在LLVM和其他地方称为“wam32”。我们正在努力实现一种“wam64”(wasm规范中的“内存64”),其中指针可以是64位的,并且我们将能够使用超过1600万TB的内存(!),但是在此之前,我们最多希望能够访问的是4 GB。似乎我们应该始终能够访问4 GB,因为这是32位指针所允许的。那么为什么我们被限制在这个数字的一半,只有2 GB呢?浏览器和工具链端都有多种原因。让我们从浏览器开始。原则上,V8中的更改听起来很简单:只需确保为WebAssembly函数生成的所有代码以及所有内存管理代码都使用无符号32位整数作为内存索引和长度,我们就应该完成工作。然而,在实践中,还有更多的事情要做!由于WebAssembly内存可以作为ArrayBuffer导出到JavaScript,因此我们还必须更改JavaScript ArrayBuffers、TypedArray以及所有使用ArrayBuffers和TypedArray的Web API的实现,如Web Audio、WebGPU和WebUSB。我们必须解决的第一个问题是,V8使用SMI(即31位有符号整数)作为TypedArray索引和长度,因此最大大小实际上是230-1,即大约1 GB。此外,事实证明,将所有内容都转换为32位整数是不够的,因为4 GB内存的长度实际上不适合32位整数。举例说明:在十进制数中,有100个数字有两位数(0到99),但是";100";本身就是一个三位数。类似地,4 GB可以用32位地址寻址,但4 GB本身就是33位数字。我们本可以满足于稍微低一点的限制,但是由于我们无论如何都要接触所有的TypedArray代码,所以我们想让它为将来更大的限制做好准备。因此,我们更改了所有处理TypedArray索引或长度的代码,以便在需要与JavaScript接口的情况下使用64位宽的整数类型或JavaScript数字。作为一个额外的好处,这意味着现在支持wam64的更大内存应该是相对简单的!第二个挑战是处理JavaScript对Array元素的特殊大小写,而不是常规命名属性,这反映在我们的对象实现中。(这是一个与JavaScript规范有关的相当技术性的问题,所以如果您没有遵循所有细节,请不要担心。)。考虑这个例子:如果array是一个纯JavaScript对象或Array,那么数组[5_000_000_000]将作为基于字符串的属性查找来处理。运行库将查找以字符串命名的属性“5000000000”。如果找不到这样的属性,它将沿着原型链向上遍历并查找该属性,或者最终在链的末尾返回UNDEFINED。但是,如果数组本身或其原型链上的对象是TypedArray,则运行库必须在索引5,000,000,000处查找索引元素,或者如果此索引超出界限,则立即返回UNDEFINED。换句话说,TypedArray的规则与普通数组有很大的不同,这种差异主要表现在索引很大的情况下。因此,只要我们只允许较小的TypedArray,我们的实现就可以相对简单;特别是,只需查看属性键一次就足以决定是应该采用索引路径还是名称为";的查找路径。为了允许更大的TypedArray,我们现在必须在沿着原型链向上移动时重复进行这种区分,这需要仔细的缓存,以避免由于重复工作和开销而降低现有JavaScript代码的速度。在工具链端,我们也必须做工作,大部分工作都在JavaScript支持代码上,而不是WebAssembly中的编译代码上。主要问题是Emscripten总是以这种形式写入内存访问:即从地址PTR+OFFSET读取32位(4字节)作为带符号整数。其工作原理是HEAP32是一个Int32Array,这意味着数组中的每个索引都有4个字节。因此,我们需要将字节地址(PTR+OFFSET)除以4来得到索引,这就是>;>;2所做的。问题是>;>;是签名操作!如果地址在2 GB或更高,则会将输入溢出为负数://略低于2 GB即可,这将打印536870911控制台。日志((2*1