转载 | 我们用WebAssembly将Web App速度提升了20倍!

image

作者|Robert Aboukhalil译者|薛命灯WebAssembly 是除 JavaScript 之外另一门可以在浏览器上运行的语言,其他语言(如 C/C++/Rust)也可以被编译成 WebAssembly 在浏览器上运行。WebAssembly 是静态类型的语言,使用线性内存,并保存成紧凑的二进制格式,所以速度非常快,可以以“接近原生”的速度运行代码(与从本地命令行运行程序的速度相当)。

到目前为止,WebAssembly 已经被用在各种应用程序中,从游戏(如 Doom 3)到将桌面应用程序移植到 Web(如 Autocad 和 Figma)。它甚至也被用到了浏览器之外,例如被作为一门高效而灵活的 Serverless 计算编程语言。

这篇文章将介绍如何使用 WebAssembly 来加速一款 Web 数据分析工具。

背景介绍

这个 Web 工具就是 fastq.bio,它是一个交互式的 Web 工具,科学家用它来快速预览 DNA 序列数据的质量。下面是这个工具的一个截图:

image.gif

我不打算深入介绍这个计算过程,但总的来说,上面的图表为科学家提供了一个有关 DNA 序列质量的信息,可以帮助他们快速发现数据质量问题。

虽然现在也有很多命令行工具可用来生成这类报告图表,但 fastq.bio 的目标是让用户可以在浏览器中直接通过交互式的方式预览数据质量,这对于不习惯使用命令行的科学家来说非常有用。

这个工具的输入是一个普通文本文件,其中包含了使用 DNA 序列指令生成的 DNA 序列和其中每个核苷酸的质量分数。这种格式被称为“FASTQ”,所以这个工具的名字叫作 fastq.bio。

JavaScript 实现

最初版本的 fastq.bio 要求用户从本地选择一个 FASTQ 文件,这个工具借助 File 对象(使用了 FileReader API)从文件的随机位置读取一小块数据,然后我们使用 JavaScript 对这个数据库执行基本的字符串操作,并计算相关的指标。这个指标可以帮助我们跟踪一个 DNA 片段中有多少 A、C、G 和 T。

在计算好指标之后,我们使用 Plotly.js 画出结果图表,然后继续读取下一个数据块。我们之所以每次只处理一小块数据,是为了获得更好的用户体验,因为一次性处理整个文件(一个 FASTQ 文件通常会有几个 GB 那么大)会让用户等待太长时间。我们发现,每次处理介于 0.5 MB 到 1 MB 之间的数据块可以让应用程序看起来是连续的,而且可以更快地为用户返回信息,但这个数字也取决于应用程序的具体细节以及计算机的处理速度。

初始架构非常简单:

image

红色方框部分就是我们要进行的字符串操作,用来生成指标。这个部分是计算密集型的,所以很适合使用 WebAssembly 来优化。

WebAssembly 实现

为了搞清楚 WebAssembly 是否可以加快 Web 应用程序的速度,我们尝试了一些现成的工具,这些工具是使用 C/C++/Rust 开发的,这样就可以把它们移植成 WebAssembly,并且这些工具已经得到科学社区的认可。

经过一些调研,我们最终决定使用 seqtk(https://github.com/lh3/seqtk),这是一个被广泛使用的开源工具,使用 C 语言开发,可以用来评估序列数据的质量。

在将 seqtk 编译成 WebAssembly 之前,我们先来看看如何从源代码编译 seqtk,并在命令行中运行它。

# Compile to binary$ gcc seqtk.c \   -o seqtk \   -O2 \   -lm \   -lz

另一方面,我们可以使用 Emscripten 工具链将 seqtk 编译成 WebAssembly:

https://emscripten.org/

如果你还没有安装 Emscripten,可以从 Dockerhub 上下载我们提供的 docker 镜像,其中就包含了这个工具链:

https://hub.docker.com/r/robertaboukhalil/emsdk/tags

或者你也可以从头开始安装,只是这样需要更长的时间:

https://emscripten.org/docs/getting_started/downloads.html

$ docker pull robertaboukhalil/emsdk:1.38.26$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26

在进入容器后,我们可以使用 emcc 代替 gcc:

# Compile to WebAssembly$ emcc seqtk.c \    -o seqtk.js \    -O2 \    -lm \    -s USE_ZLIB=1 \    -s FORCE_FILESYSTEM=1

编译成二进制文件和编译成 WebAssembly 其实并没有太多不同之处:

  1. Emscripten 会生成一个.wasm 文件和一个.js 文件,而不是生成 seqtk 二进制文件。

  2. 我们使用了 USE_ZLIB 标记,这样就可以支持 zlib 库。因为 zlib 已经被移植到 WebAssembly,并被广泛使用,所以 Emscripten 将会将其包含在项目中。

  3. 我们启用了 Emscripten 的虚拟文件系统(POSIX 风格的文件系统),只是它是运行在浏览器的内存中,在页面被刷新时会消失,除非你使用 IndexedDB 把它的状态保存在浏览器中)。

为什么使用虚拟文件系统?为了回答这个问题,我们先来比较一下在命令行中调用 seqtk 和在 JavaScript 中调用编译过的 WebAssembly 模块:

# On the command line$ ./seqtk fqchk data.fastq# In the browser console> Module.callMain(["fqchk", "data.fastq"])

访问虚拟文件系统是一个非常重要的能力,这意味着我们可以在不重写 seqtk 的情况下直接处理字符串。我们可以将数据块挂载到虚拟文件系统中(作为 data.fastq 文件),然后调用 seqtk 的 main() 函数。

在将 seqtk 编译成 WebAssembly 后的 fastq.bio 架构图:

image

如图所示,我们并没有在浏览器主线程上运行计算,而是使用了 WebWorker,这样就可以在后台线程上运行计算,避免给浏览器造成阻塞。WebWorker 的控制器负责启动 Worker,并管理与主线程之间的通信。

然后,我们让 Worker 运行 seqtk 命令来处理事先挂载好的文件。在 seqtk 运行完成之后,Worker 通过一个 Promise 将结果发送回主线程,主线程在接收到消息之后使用结果数据更新图表。与 JavaScript 实现一样,我们每次只处理一个数据块。

性能优化

为了评估使用 WebAssembly 是否确实为我们带来了速度上的优势,我们比较了 JavaScript 实现和 WebAssembly 实现每秒钟分别可以读取多少指标。我们忽略了生成交互式图表的时间,因为两者在这方面都使用了 JavaScript。

在什么都没做的情况下,我们已经可以看到 WebAssembly 比 JavaScript 有 9 倍左右的速度提升:

image.gif

这个结果已经很好了,不过,我们发现,seqtk 生成了很多有用的 QC 指标,但其中有很多并没有被用到。在移除了这些没有被用到的指标之后,速度提升达到了 13 倍。

image

最后还有一个可改进的地方。到目前为止,fastq.bio 是通过调用两个不同的 C 函数来获取指标,其中每个函数负责计算一系列不同的指标。其中一个函数以直方图的形式返回信息,另一个则以 DNA 序列位置函数的形式返回信息。这意味着同一个数据块会被读取两次,而这其实是不必要的。

所以,我们将这两个函数的代码合成一个。因为原本的两个输出包含了不同数量的列,所以我们使用 JavaScript 来区分它们。但这样做是值得的:速度提升了 20 多倍!

image

注意事项

不要指望 WebAssembly 总能为我们带来 20 多倍的速度提升,有时候可能只能获得 2 倍甚至是 20% 的提升。而如果在内存中加载了太多的数据,有可能速度还会变慢,或者需要在 WebAssembly 和 JavaScript 之间进行很多的通信。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容