《WebAssembly权威指南》(1)WebAssembly简介,下面一起来看看本站小编opendotnet给大家精心整理的答案,希望对您有帮助
《WebAssembly权威指南》连载公告
非凡的主张需要非凡的证据。
——CarlSagan博士
本章将介绍WebAssembly及其诞生的背景。从某种意义上说,它是过去七十年来Web发展的巅峰。我们有相当多的历史要介绍。如果你不喜欢历史论述可以跳过这一章,直接去看第二章,但我希望你不要这样做。理解为什么这项技术如此重要以及它的起源是很重要的。
在正式向世界介绍WebAssembly的论文中,作者指出,其动机是为了满足现代Web交付的需求,在软件方面的应用,单靠JavaScript是无法做到的。归根结底,这是一个提供软件的追求,即:
安全
快速
可移植
紧凑
我们的部署平台比以往任何时候都更加多样化,因此我们需要在代码和应用层面上的可移植性。一个通用的指令集或字节码目标可以使算法在不同的环境中工作,因为我们只需要将逻辑步骤映射到它们在特定机器架构上的表达方式。程序员们使用应用程序编程接口(API),如OpenGL、POSIX或Win32,因为它们提供了打开文件、拉起子进程或在屏幕上绘制的功能。它们很方便,减少了开发人员需要编写的代码量,但它们对提供功能的库存在依赖性。如果API在目标环境中不可用,应用程序将无法运行。这是微软能够利用其在操作系统市场上的优势,也是其在应用程序套件中占据主导地位的方法之一。另一方面,开放标准可以使软件更容易移植到不同的环境。
经过几十年的发展,开源软件的价值主张已经很清楚了。我们倾向于使用由其他开发者编写的有价值的、可重复使用的组件,以此来满足我们自己的需求。然而,并非所有可用的代码都是值得信赖的,当我们执行从互联网上下载的不值得信赖的代码时,我们就会受到软件供应链的攻击。通过Web钓鱼攻击、数据泄露、恶意软件和勒索软件,我们容易受到不安全的软件系统的风险、商业影响和个人成本的影响。
直到现在,JavaScript一直是解决其中一些问题的唯一方法。当它在沙盒环境中运行时,它给了我们某种程度的安全。它是无处不在的,可移植的。引擎已经变得更快。生态系统已经爆炸性地成为生产力的雪崩。然而,一旦你离开了基于浏览器的保护范围,我们仍然有安全问题。作为客户端运行的JavaScript代码和在服务器上运行的JavaScript是有区别的。单线程设计使长期运行或高度并发的任务变得复杂。由于它起源于一种动态语言,有几类优化可以用于其他编程语言,而这些优化现在和将来都不能作为最快和最现代的JavaScript运行时的选项。
在有些观察者眼中WebAssembly是对JavaScript的攻击,但事实并非如此。当然,如果你愿意,你可以避免使用JavaScript,但它主要是为你提供选择,用你选择的语言解决问题,而不需要一个单独的运行时,或不得不关心另一个软件是用什么语言写的。现在已经可以使用WebAssembly模块而不知道它是如何构建的。这将增加我们从软件中获得的商业价值的寿命,同时允许我们在采用新语言时进行创新,而不影响其他部分。
在过去的几十年里,我们经历了几个试图解决这些问题的工具、语言、平台和框架,但WebAssembly第一次把它做对。它的设计者并没有试图过度规范什么。他们正在从过去的经验中学习,拥抱Web,并将问题空间的思维运用到最终是一个困难的多维问题上。在我们进一步深入研究之前,让我们看看对这一令人兴奋的新技术的形成性影响。
在WebAssembly社区有一个笑话,说WebAssembly是"非Web也非汇编"。虽然这在某些方面是真的,但这个名字足以暗示它所提供的东西。它是一个目标平台,有一系列的指令,隐约有汇编的感觉。事实上,WebAssembly模块经常作为另一种类型的URL可寻址资源通过Web传递,这就证明了在名称中加入Web这个词是合理的。
"传统的软件开发"和"Web开发"之间的一个主要区别是,一旦你有了浏览器,后者实际上不需要安装。这在交付成本和面对错误和功能要求时快速转换新版本的能力方面,是一个改变游戏规则的因素。在其他跨平台的技术生态系统中,如互联网和Web,它也使支持多种硬件和软件环境变得更加容易。
万维网的发明者TimBerners-Lee爵士曾在欧洲核子研究组织(CERN)工作,在那里他提交了一份提议,关于在追求CERN更大的研究目标的过程中,将文件、图像和数据相互连接。尽管事后看来,其影响是显而易见的,但在他被要求采取行动之前,他不得不在内部多次宣传他的想法。作为一个组织,欧洲核子研究中心由世界各地的几十个研究机构代表,这些机构派科学家携带自己的计算机、应用程序和数据。没有真正的能力来强迫每个人使用相同的操作系统或平台,所以他认为需要一个技术解决方案来解决问题。
在Web之前,有诸如Archie这样的服务、Gopher和WAIS但他想象了一个更方便用户的平台,该平台最终作为互联网分层结构顶端的一个应用层面的创新而产生。他还从标准通用标记语言(SGML)中吸取了一些想法,并使它们,成为超文本标记语言(HTML)的基础。
Web的交互模型被称为超文本传输协议(HTTP)。它是基于一组受限制的动词来交换基于文本的信息。虽然这是一个简单而有效的模型,易于实现,但由于不断返回服务器的固有延迟,它很快就被认为不适合交互式现代应用的任务。能够将代码下发到浏览器的想法一直很有说服力。如果它在用户的交互中运行,那么不是每一个活动都需要返回服务器。这将使Web应用极大地提高互动性、响应性和使用的乐趣。如何实现这一点这并不完全清楚。哪种编程语言是最有意义的?我们如何平衡表达能力和浅显的学习曲线,以便更多的人能够参与到开发过程中?哪些语言比其他语言更好,我们将如何保护客户端的敏感资源不受恶意软件的侵害?
浏览器领域的大部分创新最初是由网景公司(NetscapeCommunicationsCorp)推动的。信不信由你,NetscapeNavigator最初是一个付费软件,但该公司更大的兴趣是销售服务器端软件。通过扩展客户端的功能,它可以创造和销售更强大和有利可图的服务器功能。
当时,Java正从其作为计算机设备的嵌入式语言的雏形中出现,但它还没有太多的成功记录。作为C++的简化版本,它是一个引人注目的想法,它运行在一个虚拟平台上,因此本质上是跨平台的。作为一个旨在运行通过Web下载的软件的环境,它通过语言设计、沙盒容器和细粒度的许可模型来实现安全。
在不同的操作系统之间移植应用程序是一件棘手的事情,而现在不在需要这样的前提,使人们对软件开发的未来产生了狂热的期待。太阳微系统公司发现自己处于一个令人羡慕的位置,即拥有解决各种问题和机会的完美风暴的方法。考虑到这一潜力,正在讨论将Java引入浏览器,但还没搞清具体如何实施,以及何时发布。
作为一种面向对象的编程(OOP)语言,Java包含复杂的语言特性,如线程和继承。在Netscape,有人担心这对非专业的软件开发人员来说可能太难掌握,因此该公司雇用了BrendanEich来创建一个"浏览器的方案",设想一种更容易的、轻量级的脚本语言。Brendan可以自由决定他想在该语言中包含哪些内容,但他也面临着要尽快完成的压力。一种用于交互式应用的语言被认为是这个新兴平台向前迈进的关键一步,而且每个人都希望尽快完成。正如塞巴斯蒂安-佩罗特(SebastiánPeyrott)在博文"JavaScript简史"中指出的那样,出现的是"Scheme和Self的早产儿,具有Java的外观"。
最初,浏览器中的JavaScript只限于简单的交互,如动态菜单、弹出式对话框和对按钮点击的响应。与每一个动作都要往返于服务器相比,这些都是很大的进步,但与当时在台式机和工作站上所能做到的相比,它仍然是玩具。
我在Web早期工作的公司创造了第一个整个地球的可视化环境,涉及到TB级的地形信息,超光谱图像,以及从无人机视频中提取视频帧。当然,这最初需要SiliconGraphics工作站,但在几年内,它能够在带有消费级图形处理单元(GPU)的PC上运行。在当时的Web上,这样的事情根本不可能发生,不过,由于WebAssembly的出现,这种情况已经不复存在。
随着Java小程序和JavaScript在网景浏览器中的应用,开发人员开始尝试动态网页、动画和更复杂的用户界面组件。几年来,这些仍然只是玩具性的应用,但这一愿景具有吸引力,而且不难想象它最终会导致什么。
火狐浏览器从Mozilla中脱颖而出,成为Netscape后继的一个可行的竞争者。谷歌的Chrome浏览器最终成为IE浏览器的合适替代品。每个阵营都有自己的追随者,但人们对解决它们之间的不兼容问题的兴趣越来越大。在浏览器领域引入选择,迫使每个供应商更加努力,做得更好,以超越对方,作为实现技术主导地位和吸引市场份额的手段。
因此,JavaScript引擎的速度明显提高了。尽管HTML4仍然很"古怪",在不同的浏览器和平台上使用起来也很痛苦,但现在已经开始有可能隔离这些差异了。这些发展与在基于标准的环境结构中工作的愿望相结合,鼓励了JesseJamesGarrett[2]想到了一种不同的Web开发方法。他提出了Ajax这个术语,它代表了一组标准的组合:异步JavaScript和XML。这个想法是让数据从后端系统流入前端应用程序,而前端应用程序将对新的输入作出动态响应。通过在操作DOM的层面上工作,而不是拥有一个单独的、沙盒式的用户界面空间,浏览器可以成为基于Web的客户-服务器架构中的通用应用程序消费者。
在这一时期,漫长的HTML5标准化进程也已经开始,试图提高各浏览器的一致性,引入新的输入元素和元数据模型,在其他功能中,Ajax是一个非常重要的元素,它提供了硬件加速的2D图形和视频元素。Ajax风格的融合,ECMAScript作为一种语言的出现和成熟,更容易的跨浏览器支持,以及功能越来越丰富的基于Web的环境,都引起了活动和创新的爆炸。我们看到无数基于JavaScript的应用程序框架来来去去,但就可能的情况而言,有一种稳定的前进势头。随着开发者们的努力,浏览器供应商也改进了他们的引擎,使其能够进一步推动发展。这是一个良性循环,为安全、可移植、零安装的软件系统的潜力带来了新的愿景。
随着其他障碍和限制的消除,这种处于核心位置的奇怪的小语言成为前进道路上一个越来越大的惯性阻力。引擎正在成为世界级的开发环境,具有更好的调试和性能分析的工具。新的编程范式,如基于承诺的风格,允许更好的模块化和异步友好的应用代码,在JavaScript臭名昭著的单线程环境中实现强大的结果。但是语言本身并不能像C或C++等其他语言那样进行优化。从语言性能的角度来看,有一些简单的限制,是可以实现的。
随着WebGL和WebRTC等技术的开发和采用,Web平台标准继续得到推进不幸的是,JavaScript的性能限制使它不适合在浏览器中扩展涉及低级Web、多线程代码、图形和流媒体视频编解码的功能。
正是由于这些和其他原因,谷歌开始考虑采用另一种,以实现安全、快速和可移植的客户端Web开发。
2011年,谷歌发布了一个新的开源项目,名为原生客户端(NaCl)。其想法是在浏览器中提供接近原生速度的代码执行,同时出于安全考虑在有限的权限沙盒中运行。你可以认为它有点像ActiveX,背后有一个真正的安全模型。该技术很适合谷歌的一些大目标,如支持ChromeOS和将任务从桌面应用转移到Web应用。它最初并不是要为每个人扩展开放Web的能力。
这些用例主要是为了支持基于浏览器的计算密集型软件的交付,如:
游戏
音频和视频编辑系统
科学计算和CAD系统
模拟
最初的重点是C和C++作为源语言,但由于它是基于LLVM编译器工具链的。它有可能支持其他可以生成LLVM中间表示(IR)的语言。你将看到,这将是我们向WebAssembly过渡的一个反复出现的主题。
这里有两种形式的可分发代码。第一种是同名的NaCl,它产生的"nexe"模块将针对特定的硬件架构(例如ARM或x86-64),并且只能通过GooglePlay商店分发。另一种是称为PNaCl的可移植形式。它将以LLVM的Bitcode格式表示,使其与目标无关。这些模块被称为"pexe"模块,需要在客户端的主机环境中被转换为原生架构。
该技术是成功的,因为在浏览器中展示的性能与本地执行速度的差距很小。通过使用软件故障隔离(SFI)技术,有可能从网上下载高性能、安全的代码并在浏览器中运行。一些流行的游戏,如Quake和Doom被编译成这种格式,以显示最终可能的结果。问题是,NaCl二进制文件需要为每个目标平台生成和维护,且只能在Chrome上运行的。它们还在进程外空间运行,因此它们不能直接与其他WebAPI或JavaScript代码互动。
虽然在有限权限的沙盒中运行是可以实现的,但它确实需要对二进制文件进行静态验证,以确保它们不会试图直接调用操作系统服务。生成的代码必须遵循某些地址边界对齐模式,以确保它不违反分配的内存空间。
如上所述,PNaCl模块的可移植性更高。LLVM基础设施可以生成NaCl原生代码或可移植的Bitcode,而不需要修改,也不需要修改原始源。这是一个很好的结果,但是代码的可移植性和应用程序的可移植性之间是有区别的。应用程序要求它们所依赖的API可用,以便工作。谷歌提供了一个名为PepperAPI的应用二进制接口(ABI)。用于低级别的服务,如3D图形库、音频播放、文件访问(通过IndexedDB或LocalStorage模拟)等等。虽然由于LLVM的存在,PNaCl模块可以在不同平台的Chrome浏览器中运行,但它们只能在提供合适的PepperAPI实现的浏览器中运行。虽然Mozilla最初表示有兴趣这样做,但他们最终决定尝试一种不同的方法,这就是后来被称为asm.js的方法。NaCl在推动行业向这个方向发展方面功不可没,但它最终还是太麻烦了,而且太针对Chrome,无法推动开放Web的发展。Mozilla的尝试在这方面比较成功,即使它没有提供与本地客户端方法相同的性能水平。
asm.js项目[3]至少有一部分动机是为了给Web带来一个更好的竞争故事。这很快就扩展到了一个愿望,即允许任意的应用程序安全地传递到浏览器的沙盒中,而不需要对现有的代码进行次级修改。
正如我们之前所讨论的,浏览器生态系统已经在不断进步,使2D和3D图形、音频处理、硬件加速视频等以基于标准的、跨平台的方式提供。我们的想法是,在这个环境中操作,将允许应用程序使用这些被定义为可以从JavaScript调用的任何功能。JavaScript引擎是高效的,并且有强大的沙盒环境,经历了重要的安全审计,所以没有人觉得要从头开始。真正的问题仍然是无法提前选择,所以运行时的性能可以进一步提高。
由于其动态性质和缺乏适当的整数支持,在代码加载到浏览器之前,有几个性能障碍无法得到有意义的管理。一旦这样做了,及时优化(JIT)编译器就能很好地加速,但仍然存在固有的问题,比如缓慢的边界检查数组引用。虽然整个JavaScript不能被提前优化,但它的一个子集可以被优化。
#include
brian@tweezer~/s/w/ch01>emcchello.cbrian@tweezer~/s/w/ch01>nodea.out.jsHello,world!很酷,不是吗?
只是有一个问题:
brian@tweezer~/s/w/ch01>ls-laha.out.js-rw-r--r--1brianstaff119KAug1719:08a.out.js119KB,这是一个非常大的"Hello,world!"程序。快速浏览一下本地可执行文件可能会让你感觉到发生了什么。
brian@tweezer~/s/w/ch01>clanghello.cbrian@tweezer~/s/w/ch01>ls-laha.out-rwxr-xr-x1brianstaff48KAug1719:11a.out为什么我们所谓的优化的JavaScript程序比本地版本大了近三倍?这不仅仅是因为作为一个基于文本的文件,JavaScript的体积更大。再看一下这个程序。
2.对printf()函数的引用将由一个在运行时加载的动态库来满足。
如果我们使用nm查看编译后的可执行文件中定义的符号,我们会发现二进制文件中不包含printf()函数的定义。它被标记为"U",表示"未定义"。
brian@tweezer~/s/w/ch01>nm-aa.out0000000100002008ddyld_private0000000100000000Tmh_execute_header0000000100000f50T_mainU_printfUdyld_stub_binder当Clang生成可执行文件时,它留下了一个占位符,指向它期望由操作系统提供的函数。对于浏览器来说,没有可用的标准库,至少在动态加载的意义上没有,所以必须提供库函数和它需要的任何东西。此外,这个版本不能直接与浏览器的控制台对话,所以它需要被赋予钩子来调用一个函数,如浏览器的console.log()功能。为了让它能在浏览器中工作,该功能必须与应用程序一起交付,这就是为什么它最终会变得如此庞大。
这很好地突出了可移植代码和可移植应用程序之间的区别,这是本书的另一个共同主题。现在,我们可以感叹它的工作原理,但本书不叫*asm.js:TheDefinitiveGuide*是有原因的。asm.js是一块了不起的垫脚石,它证明了从各种可优化的语言中生成性能合理的沙盒化JavaScript代码是可能的。Java脚本本身也可以进一步优化,这是超集所不能做到的。通过基于LLVM的工具链和自定义的后端来生成这个子集,所付出的努力要比原来小得多。
asm.js代表了不支持WebAssembly标准的浏览器的一个很好的后备位置,但现在是时候抛出本书主题了。
通过NaCl,我们找到了一个能提供沙盒和性能的解决方案。通过PNaCl,我们发现了平台的可移植性,但没有浏览器的可移植性。通过asm.js,我们发现了浏览器的可移植性和沙箱,但没有同样的性能水平。我们还被限制在处理JavaScript,这意味着如果不首先改变语言本身,我们就不能用新的功能来扩展这个平台(例如,高效的64位整数)。考虑到这是由一个国际标准组织管理的,这不可能是一个快速周转的方法。
正是在这种情况下,2015年,除了JavaScript的创造者BrendanEich之外,其他人都宣布已经开始了WebAssembly的工作。他强调了这项工作的几个具体原因,并称其为"低级安全代码的二进制语法,最初与asm.js共同表达,但从长远来看,能够与JS的语义相分离,以便最好地作为多种源级编程语言的通用对象级格式。"
他继续说:"可能的长期分歧的例子:零成本异常、动态链接、调用/cc。是的,我们的目标是开发Web的多语言编程语言对象文件格式。"
至于为什么这些各方对此感兴趣,他提出了这样的理由。"asm.js很好,但一旦引擎为其优化,解析器就会成为热点——在移动设备上非常热。传输压缩是需要的,可以节省带宽,但在解析前的解压却会造成伤害"。
最后,也许公告中最令人惊讶的部分是谁将参与其中:
一个W3C社区小组,WebAssemblyCG,向所有人开放。从GitHub的日志中可以看到,WebAssembly到目前为止是由谷歌、微软、Mozilla和其他一些人共同完成的。我很抱歉,这项工作一开始是通过GitHub的私人账户完成的,但这是一个临时措施,以帮助几家大公司达成共识,并接受长期的合作游戏,必须玩这个游戏才能成功。
在建立这个平台的过程中,潜在的复杂问题是无止境的。我们并不完全清楚到底应该在前面指定什么。不是每一种语言都支持原生线程。并非每种语言都使用异常。C/C++和Rust就是例子,它们的运行时不需要垃圾收集。魔鬼总是在细节中,但合作的意愿是存在的。而且,正如他们所说的,有意愿的地方就有办法。