五月22,2019 javascript
做者:Senthil Padmanabhan和Pranav Jha前端
从发布之日起,WebAssembly 就在前端世界引发了巨大轰动。Web 社区欣然接受了接受用 JavaScript 之外的其余编程语言为浏览器编写的并运行代码的想法。首先,WebAssembly 始终保证本机速度比 JavaScript 快得多。在咱们的 eBay 上也同样。java
咱们的工程师对此想法感到很是兴奋,并一直关注着规格及其发展。一旦将WebAssembly 1.0 运用于全部主流浏览器后,eBay周围的团队就渴望尝试一下。git
有一个问题:虽然有不少用例从 WebAssembly 中受益,但在电子商务中的技术范围仍然很显得很原始。咱们找不到合适的用例来利用 WebAssembly。提出了一些建议,可是 JavaScript 自己就更好。在 eBay 上,当咱们评估新技术时,咱们要问的第一个问题是“这为咱们的客户增长了哪些潜在价值?” 除非对此有明确的了解,不然咱们不会继续进行下一步。很容易被新的闪亮事物带偏,经常忘记这一事实可能对咱们的客户没有任何影响,而只会使现有的工做流程变得复杂。用户体验始终赛过开发人员体验。可是 WebAssembly 是不一样的。它具备巨大的潜力,咱们只是没有合适的用例。好吧,最近发生了变化。github
iOS 和 Android 上的 eBay 本地应用程序在销售流程中均具备条形码扫描功能。用设备摄像头扫描产品 UPC 条形码并自动填写表单,从而消除了人工开销。这是仅本机应用程序的功能。须要在设备上进行一些密集的图像处理才能从相机流中检测条形码编号。而后将检索到的代码发送到后端服务,完成表单填写。这意味着设备上的图像处理流程必须具备很高的性能。对于本机应用程序,咱们将内部构建的 C++ 扫描程序库编译为适用于 iOS 和 Android 的本机代码。从相机流识别出产品条形码的性能很是好。咱们正在逐步过渡到 iOS 和 Android 本机 API,可是 C++ 库仍然是最可靠的。web
条形码扫描对咱们的卖家来讲是一种直观的功能,由于它使表单流程更加流畅。不幸的是,咱们的 Web 移动端用户未启用此功能。除了没有条形码扫描,咱们已经为 Web 移动端提供了优化的销售流程,而卖方必须手动输入产品UPC,从而增长了使用的阻力。docker
以前,咱们已经研究过为移动网络实施条形码扫描功能。实际上,咱们使用开源 JavaScript 库BarcodeReader推出了条形码扫描功能。这是两年前的事了。问题在于它仅在20%的时间内表现良好。其他80%的时间很是慢,用户认为它已损坏。超时是常态。这也不出所料,由于 JavaScript 确实能够和本机代码同样快,可是仅当它处于“热路径”时,即由JIT进行了优化的编译。根源在于,JavaScript 引擎使用大量启发式方法来肯定代码路径是否“热”,而且不能保证每一个实例都获得优化。这种不一致显然令用户很无奈,所以咱们不得不由用了该功能。可是如今状况有了新变化。随着 Web 平台的快速发展,这个问题从新浮出水面:“咱们可否为 Web 实施性能一致的条形码扫描功能?”npm
一种选择是等待Shape Detection API。提出的 Web API 为 Web 带来了许多本机图像检测功能,其中之一是条形码检测。但还处于起步阶段,要实现跨浏览器兼容性还有很长的路要走。即便这样,也不能保证在每一个平台上都能正常使用。所以,咱们必须考虑其余选项。编程
这恰是 WebAssembly 发挥期所长地方。若是在 WebAssembly 中实现了条形码扫描功能,咱们能够保证其性能始终如一。WebAssembly 字节码的强类型和不变结构,使编译器始终停留在热路径上。最重要的是,咱们有一个现有的 C++ 库正在为本机应用程序完成这项工做。C++ 库是编译成WebAssembly 的理想选择。咱们认为咱们面前有一条光明的大道。...好吧,也许还不彻底是。后端
咱们实现基于 WebAssembly 的条形码扫描的项目设计很是简单。
.wasm
文件。.wasm
文件。WebAssembly工做流程
汇编
任何 WebAssembly 项目的第一步都是拥有定义明确的编译管道。Emscripten 已成为事实上的用于编译 WebAssembly 的工具链,可是关键是要有一个一致的环境来产生肯定性的输出。咱们的前端基于 Node.js,这意味着咱们须要一个与 npm 工做流程一块儿使用的解决方案。幸运的是,大约在同一时间,Surma Das发表了“ Emscripten and npm ”一文。基于Docker的 WebAssembly 编译方法很是合理,由于它消除了大量的开销。在文章中建议,咱们用由trzeci提供的 Emscripten 的Docker镜像。咱们必须对自定义 C++ 库进行一些调整,以使其兼容于WebAssembly。所谓调整就主要是反复试验。最终,咱们可以进行编译,而且可以在咱们现有的构建管道中创建简洁的 WebAssembly 工做流。
很快,可是…
咱们计算扫描功能的性能的方法是经过分析 wasm API 每秒能够处理的帧数。即 wasm API 接收一帧来自实时摄像机流的图像快照像素数据,执行计算并返回响应。连续进行此操做,直到检测到条形码为止。咱们以众所周知的【每秒帧数】(FPS)度量标准对其进行度量。
在咱们的测试中,WebAssembly 实现平均以惊人的 50 FPS 执行。可是,在当时的超时阈值下,仅有60%的状况能正确识别。即便具备如此高的 FPS,它也没法快速检测出剩余40%有效扫描的条形码,最终只能无奈显示警告消息。为了进行比较,咱们以前尝试的 JavaScript 实施大多数状况下仅以1 FPS 执行。所以,能够确定的是,WebAssembly 速度更快(50倍),但因为某种缘由,它没法在分配的时间内检测到近一半的条形码。还应该提到的是,在某些状况下,JavaScript 表现很是出色,而且可以当即检测到条形码。一种明显的选择是延迟显示警告消息,但这只会增长用户的沮丧感,并且咱们实际上并无解决真正的问题。因此咱们放弃了这个主意。
最初,咱们不知道为何对于本机应用程序运行良好的自定义 C++ 库没法在 Web 上产生相同的结果。通过大量的测试和调试,咱们发现聚焦对象的角度以及背景阴影决定了成功检测的时间。那它如何在本机应用程序中工做?好吧,在本机应用程序中,咱们使用内置的 API 来自动聚焦或将用户的点击聚焦提供给被扫描对象的中心。这使本机应用程序能够始终将高质量的图像像素数据(即仅有关条形码的信息)发送到扫描库。这避免了图像模糊的状况。所以,始终如一的快速响应时间。
既而咱们对这件事情有了一个认识,咱们认为也许其余的本地库在不一样的聚焦条件下可能会表现更好。开源条形码阅读器ZBar很是受欢迎且稳定。更重要的是,它适用于模糊和颗粒状图像。为何不试试呢?因为咱们已经创建了 WebAssembly 工做流程,所以能够无缝编译和部署 ZBar,由于WebAssembly 是无缝的。而后,咱们开始评估 ZBar 实现。性能不错,大约 15 FPS(不如咱们的自定义 C++ 库)。可是,对于相同的超时阈值,成功率接近80%。绝对是对咱们自已的 C++ 库的改进,但仍不是100%可靠。
咱们仍然对结果不满意,可是咱们发现了一些意想不到的事情。在 ZBar 超时的状况下,自定义 C++ 库可以很是快地完成工做。这真是一个惊喜。显然,基于图像快照的质量,这两个库的执行状况有所不一样。这带给了咱们一个新办法。
用多线程再抢救一下
您可能猜对了。为何不建立两个 Web Worker 线程-一个用于 ZBar,一个用于自定义 C++ 库-并使它们相互竞争。中奖响应(即第一个发送有效条形码的响应)被发送到主线程,而且全部工做线程都被终止。咱们进行了设置,并开始进行内部测试,以模拟尽量多的状况。当扫描有效的条形码时,此设置为咱们提供了95%的成功率。比咱们之前的成功率要好得多,但仍不到100%。
一个奇怪的建议是也将原始的 JavaScipt 库加入了组合。这将使其成为三个线程。老实说,咱们认为这不会有所收获。可是,因为咱们对工做人员界面进行了标准化,所以很容易尝试。令咱们惊讶的是,三个线程相互竞争,成功率确实接近 100%。这又是彻底出乎意料的。正如文章前面提到的那样,JavaScript 在某些状况下确实表现良好,而且这个因素彷佛能够弥补上面的差距。所以,是的,“始终押在 JavaScript 上。” 开个玩笑,下图很好地概述了咱们实现的最终体系结构。
基于Web的条形码扫描仪架构
下图显示了高级流程图:
条形码扫描仪流程图
关于资源文件加载的说明
呈现主页后,将预取条形码扫描功能所需的资源。这是为了确保快速加载卖家着陆页,并准备进行交互。在页面的加载事件以后,使用XMLHttpRequest预取并缓存 WebAssembly 资源文件(wasm文件和关联的粘合代码脚本)和 JavaScript 扫描功能库 。这里要注意的一点是它们没有执行。这是为了使主线程保持空闲以进行用户交互。仅当用户点击条形码图标时才会执行。若是用户在加载资源文件以前点击条形码图标,咱们将按需加载并当即执行。条形码事件处理程序和工做线程控制程序捆绑在一块儿做为初始页面加载的一部分,可是它们的尺寸很小。
通过全面测试和内部测试后,该功能做为 A/B 测试启动。实验中的“测试方”输入区显示了条形码扫描仪图标(下面的屏幕截图),而“控制方”没有显示。
最终产品
用来评估 A/B 测试成功与否的度量标准称为“草稿完成率”。这是列表从草稿阶段到成功完成并提交的比例。草案完工率是一个很好的指标,能够支持如下观点:减小使用阻力并证实经过条形码扫描仪的无缝销售流应该可以完成更多清单。咱们将测试运行了几个星期,当结果返回时,确实很是使人满意。它与咱们最初的假设彻底一致。启用条形码扫描器后,表单流程的草稿完成率提升了30%。
A/B 测试结果
咱们还添加了埋点,以获取有关哪一种类型的扫描功能获胜的信息。结果符合预期,ZBar 占成功扫描的 53%,其次是自定义 C++库,占34%,最后是JavaScript 库,占13%。
WebAssembly 的整个旅程对咱们来讲是一次很棒的学习经历。工程师对新技术感到很是兴奋,并当即想尝试一下。若是相同的技术对以客户为中心的指标产生积极的影响,那将是双重荣幸。这暗示了本文的早期观点。技术发展很是迅速。天天咱们都会听到新事物的发布。可是只有少数几个对客户有所帮助,WebAssembly 就是其中之一。这是咱们今后练习中学到的最大经验- “对99件事情说【不】,对客户真正重要的一件事情说【是】。”
下一步,咱们正在考虑将条形码扫描功能扩展到移动端的买家,这将使买家能够扫描物品进行搜索和购买。咱们还将研究经过 Shape Detection API 和其余浏览器内置摄像头功能来加强此功能。同时,咱们很高兴在eBay 上找到了 WebAssembly 的正确用例,并将该技术引入电子商务。
特别感谢Surma Das和Lin Clark在 WebAssembly 上发表的许多文章。它确实帮助咱们在各类状况下畅通无阻。