… 尤其是在 pthreads(Mozilla pthreads,Chromium pthreads)和 SIMD(simd.js,Chromium SIMD,simd.js in asm.js)即将进入 JavaScript 的情况下。
WebAssembly 提供了两个主要好处
WebAssembly 正在考虑的二进制格式可以比 JavaScript 解析快得多地进行本地解码(实验 显示快了 20 倍以上)。在移动设备上,大型编译代码可能只需 **解析** 就需要 20-40 秒的时间,因此本地解码(尤其是在结合其他技术(如 流 以实现比 gzip 更好的压缩)时)对于提供良好的冷启动用户体验至关重要。
通过避免同时使用 AOT-可编译性 和在没有 特定 asm.js 优化 的引擎上也能获得良好性能的 asm.js 约束,新的标准使得添加达到原生性能级别的 功能 变得 **容易得多**。
当然,每个新标准都会引入新的成本(维护、攻击面、代码大小),这些成本必须通过优势来抵消。WebAssembly 通过设计允许(但不强制)浏览器在它 **现有的** JavaScript 引擎中实现 WebAssembly 来最大程度地降低成本(从而重复使用 JavaScript 引擎现有的编译器后端、ES6 模块加载前端、安全沙盒机制和其他支持的 VM 组件)。因此,在成本方面,WebAssembly 应该与大型的 JavaScript 新功能相当,而不是对浏览器模型的根本扩展。
比较两者,即使对于已经优化了 asm.js 的引擎来说,优势也超过了成本。
WebAssembly 是针对 各种用例 设计的。
我们认为可以。早期的 原型 具有演示 [1,2],表明将二进制 WebAssembly 格式解码为 asm.js 可以非常高效。随着 WebAssembly 设计的变化,已经有了对填充进行 更多 实验。
总的来说,对于 WebAssembly 在浏览器中的快速采用,乐观情绪一直在增加,这很棒,但这降低了开发填充的动力。
事实上,将 WebAssembly 填充为 asm.js 的紧迫性较低,因为存在替代方案,例如,反向填充 - 将 asm.js 编译为 WebAssembly - 存在,它允许发布一个可以作为 asm.js 或 WebAssembly 运行的单一构建。也可以通过简单地在 emscripten 中 切换开关 来构建两个并行的 asm.js 和 WebAssembly 构建项目,从而完全避免客户端上的填充时间。对于非高性能代码,第三种选择是使用编译的 WebAssembly 解释器(例如 binaryen.js)。
但是,WebAssembly 填充仍然是一个有趣的想法,原则上应该是可行的。
如 高级目标 中所述,为了实现最小可行产品,最初的重点是 C/C++。
但是,通过 与 ES6 模块接口集成,Web 开发人员无需编写 C++ 即可利用其他人编写的库;重复使用模块化的 C++ 库可以像 使用来自 JavaScript 的模块 一样简单。
除了 MVP 之外,另一个 高级目标 是改进对 C/C++ 以外的语言的支持。这包括 允许 WebAssembly 代码分配和访问垃圾回收(JavaScript、DOM、Web API)对象 。即使在 GC 支持添加到 WebAssembly 之前,也可以将语言的 VM 编译为 WebAssembly(假设它是用可移植的 C/C++ 编写的),并且已经证明了这一点(1,2,3)。但是,“编译 VM”策略会增加分发代码的大小,会失去浏览器开发者工具的集成,可能会出现跨语言循环收集问题,并且会错过需要与浏览器集成的优化。
WebAssembly 最初侧重于 C/C++,并且正在上游 clang/LLVM 中开发新的、干净的 WebAssembly 后端,然后可以被基于 LLVM 的项目(如 Emscripten 和 PNaCl)使用。
随着 WebAssembly 的发展,它将支持 C/C++ 以外的更多语言,我们希望其他编译器也能支持它,即使是针对 C/C++ 语言,例如 GCC。WebAssembly 工作组发现更容易从 LLVM 支持开始,因为他们从 Emscripten 和 PNaCl 工作中积累了更多关于该工具链的经验。
我们希望专有编译器也能获得 WebAssembly 支持,但我们会让供应商谈论他们自己的平台。
WebAssembly 社区组 很乐意与更多编译器供应商合作,将他们的意见纳入 WebAssembly 本身,并与他们一起处理 ABI 事宜。
是的!WebAssembly 定义了一个 文本格式,当开发者在任何开发者工具中查看 WebAssembly 模块的源代码时,该格式会被渲染出来。此外,文本格式的一个具体目标是允许开发者手动编写 WebAssembly 模块,以进行测试、实验、优化、学习和教学。事实上,通过删除所有 asm.js 验证所需的强制转换,WebAssembly 文本格式应该比 asm.js 更自然地阅读和编写。在浏览器之外,将文本和二进制文件相互转换的命令行和在线工具也将随时可用。最后,作为 WebAssembly 工具故事 的一部分,也正在考虑可扩展的源映射形式。
现有的 Emscripten 用户可以通过切换标志来选择将他们的项目构建为 WebAssembly。最初,Emscripten 的 asm.js 输出将被转换为 WebAssembly,但最终 Emscripten 会在整个管道中使用 WebAssembly。这种无缝过渡得益于 高级目标,即 WebAssembly 与 Web 平台良好集成(包括允许对 JavaScript 进行同步调用),这使得 WebAssembly 与 Emscripten 当前的 asm.js 编译模型兼容。
不!WebAssembly 被设计为 JavaScript 的补充,而不是替代品。虽然 WebAssembly 会随着时间的推移允许将许多语言编译到 Web 上,但 JavaScript 拥有巨大的动力,并将继续成为 Web 上的唯一、特权的(如 上面 所述)动态语言。此外,预计 JavaScript 和 WebAssembly 将以多种配置一起使用
LLVM 编译器基础设施具有许多吸引人的特性:它有一个现有的中间表示(LLVM IR)和二进制编码格式(位代码)。它具有针对许多体系结构的代码生成后端,并且由一个大型社区积极开发和维护。事实上,PNaCl 已经使用 LLVM 作为其二进制格式的基础。但是,LLVM 被设计为满足的目标和需求与 WebAssembly 的目标和需求略有不同。
WebAssembly 对其指令集体系结构 (ISA) 和二进制编码有几个要求和目标
LLVM IR 的设计初衷是为了简化编译器优化实现,并表示 C、C++ 和其他语言在多种操作系统和体系结构上所需的结构和语义。这意味着默认情况下 IR 并非可移植的(同一程序在不同体系结构上具有不同的表示形式),也不稳定(它会随着优化和语言要求的变化而改变)。它包含了大量用于实现中级编译器优化但对代码生成无用的信息(但这对代码生成实现者来说是一个很大的负担)。它还存在未定义行为(很大程度上类似于 C 和 C++),这使得某些类别的优化变得可行或更强大,但会导致运行时出现不可预测的行为。LLVM 的二进制格式(位码)是为 IR 的临时磁盘序列化而设计的,用于链接时优化,而不是为了稳定性或可压缩性(尽管它确实具有一些针对这两种特性的功能)。
这些问题都不是不可克服的。例如,PNaCl 定义了 IR 的一个小型的可移植 子集,具有减少的未定义行为,以及位码编码的稳定版本。它还采用了几种技术来提高启动性能。但是,每个定制、变通方案和特殊解决方案都意味着从通用基础设施中获得的收益更少。我们相信,通过借鉴我们在 LLVM 上的经验,并为我们的目标和需求设计 IR 和二进制编码,我们可以比采用为其他目的设计的系统做得更好。
请注意,此讨论适用于将 LLVM IR 用作标准化格式。LLVM 的 clang 前端和中级优化器仍然可以用于从 C 和 C++ 生成 WebAssembly 代码,并且将在其实现中使用 LLVM IR,类似于 PNaCl 和 Emscripten 今天的方式。
优化编译器通常具有快速数学标志,允许编译器放宽有关浮点的规则,以便更积极地优化。这可能包括假设 NaN 或无穷大不会出现,忽略负零和正零之间的区别,进行改变舍入方式或溢出发生时间的代数操作,或用更便宜的计算近似值替换运算符。
这些优化实际上引入了不确定性;无法确定代码的行为,除非知道优化器做出的具体选择。这在原生代码场景中通常不是一个严重的问题,因为所有不确定性在生成原生代码时都会被解决。由于大多数硬件没有浮点不确定性,开发人员有机会测试生成的代码,然后依靠它在所有用户中始终如一地运行。
WebAssembly 实现运行在用户侧,因此开发人员没有机会测试代码的最终行为。此级别的非确定性会导致分布式 WebAssembly 程序在不同实现中表现出不同的行为,或者随着时间的推移而发生变化。WebAssembly 在权衡利弊的情况下确实 存在一些不确定性,但快速数学标志被认为并不重要到足以包括在内。
sin
、cos
、exp
、pow
等。WebAssembly 对此类函数的策略是允许它们作为 WebAssembly 本身的库例程实现(请注意,x86 的 sin
和 cos
指令很慢且不精确,因此一般来说 nowadays 都会避免使用它们)。希望在 WebAssembly 上使用更快且不太精确的数学函数的用户,只需选择一个这样做的数学库实现即可。add
、sub
或 mul
不用担心 NaN 并不会使它们更快,因为 NaN 在所有现代平台的硬件中都得到了快速且透明的处理。mmap
呢?The mmap
系统调用具有许多有用的特性。虽然这些特性都打包在 POSIX 中的一个重载系统调用中,但 WebAssembly 将此功能分解为多个运算符。
grow_memory
运算符扩展线性内存的能力开始;0
到 memory_size
的连续范围内的页面的保护和映射。The mmap
中的一个重要功能是,上述列表中缺少了分配不连续的虚拟地址范围的能力。省略此功能的原因是
mmap
,其表现为非连续内存分配(但在幕后只是协调使用 memory_resize
和 mprotect
/map_file
/map_shmem
/madvise
)。允许非连续虚拟地址分配的好处是,如果它允许引擎将 WebAssembly 模块的线性内存与同一进程中的其他内存分配交织在一起(为了减轻虚拟地址空间碎片)。但这有两个问题
size_t
?那么,用于保存抽象的 size_t
的线性内存量也需要由抽象来确定,然后将线性内存地址空间划分为用于不同目的的段将更加复杂。每个段的大小将取决于其中存储了多少个 size_t
大小的对象。理论上这是可行的,但这会增加复杂性,并且应用程序启动时需要做更多工作。
此外,允许应用程序静态地知道指针大小可以使它们更积极地进行优化。当优化器具有指针位宽的完整信息时,它们可以更好地折叠和简化整数表达式。此外,了解各种类型的内存大小和布局可以让人知道各种指针类型中存在多少个尾部零。
此外,C 和 C++ 与抽象的 size_t
的概念存在深刻的冲突。诸如 sizeof
之类的结构需要在编译器的前端完全计算,因为它们可以参与类型检查。甚至在更早之前,通常会有预定义的宏来指示指针大小,允许代码在编译的最早阶段为指针大小进行专门化。一旦专门化完成,信息就会丢失,从而破坏了引入抽象的尝试。
最后,如果需要并且实际情况允许,仍然可以在将来添加抽象的 size_t
。
大量应用程序永远不需要 4 GiB 的内存。强制所有这些应用程序为它们存储的每个指针使用 8 字节将显著增加它们所需的内存量,并降低它们对重要硬件资源(例如缓存和内存带宽)的有效利用率。
这里所述的动机和性能影响应该与促使为 Linux 开发 x32 ABI 的动机和性能影响基本相同。
即使是 Knuth 也认为有必要在某个时刻给我们他的意见,关于 64 位指针的讨论。
是的,但这取决于 WebAssembly 嵌入器。在浏览器内部,你将能够访问与通过普通 JavaScript 可以访问的相同 HTML5 和其他特定于浏览器的 API。但是,如果 wasm VM 由特定供应商作为 “应用程序执行平台” 提供,它可能会提供对例如 Android/iOS 的 专有平台特定 API 的访问。