原文 : How We Used WebAssembly To Speed Up Our Web App By 20X (Case Study)
Author: www.smashingmagazine.com/contact/
原 文 : How to Use WebAssembly to Improve the Performance of Our Web Applications by 20 times
Translator: Zavier Tang
Overview: In this article, we’ll explore how to speed up Web App applications by replacing slow JavaScript programs with compiled WebAssemblies.
If you haven’t heard of WebAssembly, here’s an introduction: WebAssembly is a new language that runs in browsers with JavaScript. That’s right! This means that JavaScript is no longer the only language that runs in the browser!
However, apart from its name being different from JavaScript, it is unique in that you can compile WebAssembly from C/C++ / Rust and many more languages and run them in a browser. Because WebAssembly is statically typed, uses linear memory, and is stored in smaller binary formats, it can run very fast, possibly close to the speed of native programs (that is, close to running binaries on the command line). Leveraging existing tools and libraries in the browser, and the associated speed potential, are two reasons why WebAssembly is so powerful.
So far, WebAssembly has been used for a variety of applications, from games (like Doom 3) to porting desktop applications to the Web (like Autocad and Figma). It can even be used outside of the browser, for example as an efficient and flexible language for Serverless Computing.
This article is an example of a Web tool that uses WebAssembly to speed up data analysis. To do this, we’ll use a program written in C that performs the same calculation, compile it into WebAssembly, and use it instead of slow JavaScript.
Note: This article delves into some advanced topics, such as compiling C code. But don’t worry if you don’t have this experience, you can still learn about WebAssembly features together.
background
The Web application we will use is Fastq.bio, an interactive Web tool that provides scientists with a quick preview of the quality of DNA sequencing data. Sequencing is the process by which we read the “letters” (or nucleotides) in a DNA sample.
Here’s a screenshot of the application (see larger version) :
We won’t go into the details of the calculations, but simply put, the image above provides scientists with a visual exchange experience of the sequencing process and is used to quickly identify data quality.
While there are many command-line tools that can generate such quality control reports, the goal of FastQ is to provide an interactive preview of data quality without leaving the browser. This is useful for scientists unfamiliar with the command line.
The input to the application is a plain text file, output by the sequencer, containing a list of DNA sequences and the mass fraction of each nucleotide in the DNA sequence. The format of the file is called “FASTQ,” so the tool is called Fastq.bio.
If you are interested in the “FASTQ” format, check out the Wikipedia Page to learn more.
Implement Fastq.bio in JavaScript
In the original version of Fastq.bio, users first selected a Fastq file from their computer. With File objects, the application reads a small piece of data (using the FileReader API) starting from a random byte position. In this data block, we use JavaScript to perform basic string operations and calculate metrics. One metric helps us track the amount of A, C, G, and T that we typically see at each location of A DNA fragment.
Once the metrics have been calculated for the data block, we interactively plot the results with Plotly.js and move on to the next block in the file. The reason for splitting files into smaller chunks is simply to improve the user experience: processing the entire file at once would take too long, since FASTQ files are typically several hundred GIGABytes. We found that a block size between 0.5 MB and 1 MB would make the application more computationally perfect and would return information to the user more quickly, but this number size would vary depending on the details of the application and the amount of computation.
The architecture of our initial JavaScript implementation was fairly simple:
Fastq.bio JavaScript implementation architecture. (View larger version)
The red box is where we perform string operations to generate metrics. This box is a computationally intensive part of your application, which naturally makes it a target for runtime optimization using WebAssembly.
Implement Fastq.bio with WebAssembly
To explore whether we could leverage WebAssembly to speed up our Web applications, we searched for an off-the-shelf tool to calculate QC metrics on FASTQ files. Specifically, we looked for a tool written in C/C++ / Rust so that it could be ported to WebAssembly, and that tool was proven and trusted by the scientific community.
After some research, we decided to use SEQTK, a common open source tool written in C that helps us evaluate the quality of sequencing data (often used to manipulate these data files).
Before compiling to WebAssembly, let’s first consider how to compile SEQTK into a binary so that we can run it on the command line. Based on the build file, you need to execute the following GCC command:
# Compile to binary
$ gcc seqtk.c \
-o seqtk \
-O2 \
-lm \
-lz
Copy the code
On the other hand, to compile SEQTK into WebAssembly, we can use Emscripten Toolchain, which provides an alternative to existing build tools, making it easier to work in WebAssembly. If you don’t have Emscripten installed, you can download the Docker image we prepared on Dockerhub, which has the tools you need (you can also install it from scratch, but it usually takes a while) :
$docker pull robertaboukhalil/emsdk: 1.38.26 $docker run - dt - name wasm - seqtk robertaboukhalil/emsdk: 1.38.26Copy the code
Inside the container, we can use the EMCC compiler instead of GCC:
# Compile to WebAssembly
$ emcc seqtk.c \
-o seqtk.js \
-O2 \
-lm \
-s USE_ZLIB=1 \
-s FORCE_FILESYSTEM=1
Copy the code
As you can see, compiling to binary is very small compared to WebAssembly:
- We asked Emscripten to generate one
.wasm
和.js
To handle instantiation of WebAssembly modules rather than output binariesseqtk
- To support the Zlib library, we use
USE_ZLIB
Mark; Zlib is so common that it has been ported to WebAssembly and Emscripten will include it in our project - We enabled Emscripten’s virtual file system, which is a POSIX-like file system (source code here) but runs in the browser’s RAM and disappears when you refresh the page (unless you use IndexedDB to save its state in the browser, But that should be discussed in another article).
Why virtual file systems? To answer this question, let’s compare how to call seQTK from the command line with how to call a compiled WebAssembly module using JavaScript:
# On the command line
$ ./seqtk fqchk data.fastq
# In the browser console
> Module.callMain(["fqchk"."data.fastq"])
Copy the code
Accessing the virtual file system is powerful because it means we don’t have to rewrite SEQTK to handle string input. We can mount a set of data on the virtual file system as a file data.fastq and simply call the main() function of seqTK.
After compiling SeQTK into WebAssembly, here is the new Fastq.bio:
Fastq.bio for WebAssembly + WebWorkers
As shown in the figure, instead of running computations in the main thread of the browser, we use WebWorkers, which allow us to run computations in background threads without negatively impacting the responsiveness of the browser. Specifically, the WebWorker controller starts the Worker and manages communication with the main thread. On one end of Wroker, the request it receives is executed using the relevant API.
We can then ask the Worker to run the seqtk command on the file we just mounted. When seQTK is finished running, the Worker sends the result back to the main thread with a Promise. Once the message is received, the main thread outputs the results to update the chart. Similar to the JavaScript version, we process the file in blocks and update the visual view with each iteration.
To optimize the
To assess whether there is any benefit to using WebAssembly, we compare JavaScript to WebAssembly in terms of how many reads can be processed per second. We ignore the time it takes to generate interactive graphics because both implementations use JavaScript for this purpose.
Out of the box, about 9 times faster:
With WebAssembly, we can see a nine-fold increase in speed compared to our original JavaScript implementation. (View larger version)
This is good because it’s relatively easy to implement (as long as you understand WebAssembly).
Next, we noticed that although SeQTK outputs a lot of QC metrics that are usually useful, our application doesn’t actually use or plot them. By removing the output of some metrics we don’t need, we can see a much higher speed, 13 times:
Removing unnecessary output can further improve performance. (View larger version)
This is another big improvement because it is easy to implement by commenting out unwanted output statements.
Finally, we looked at another improvement. So far, Fastq.bio has obtained the relevant metrics through two different C functions, each of which computes a different set of metrics. Specifically, one function returns information as a histogram (that is, a list of values we put into a range), while the other function returns information as a function of the position of the DNA sequence. Unfortunately, this means that the same file block is read twice, which is unnecessary.
Therefore, we combined the code for both functions into one (albeit confusing) function. Since the two outputs have different number of columns, we make some distinctions on the JavaScript side to separate them. But it was worth it: we achieved more than 20x acceleration by doing so!
Finally, making your code read each file block only once can improve performance by more than 20 times. (View larger version)
Pay attention to
Now is a good time to warn. When you use WebAssembly, don’t always expect 20x acceleration. You might only get a 2x acceleration or a 20% acceleration. If you load very large files in memory, or need a lot of communication between WebAssembly and JavaScript, this can be slow.
conclusion
In short, we’ve seen that calling a compiled WebAssembly instead of a slow JavaScript program can significantly improve speed. Because the code required for these calculations is already implemented in C, we have the added benefit of reusing trusted tools. As we also mentioned, WebAssembly is not always the right tool for this job, so use it wisely.
read
- “Level Up With WebAssembly,” Robert Aboukhalil A practical guide to building Web assembly applications.
- Aioli (on GitHub) Framework for building rapid genome network tools.
- fastq.bio source code (on GitHub) An interactive Web tool for quality control of DNA sequencing data.
- “An Abridged Cartoon Introduction To WebAssembly,” Lin Clark
Translator’s note: JavaScript Weekly is in translation…
Please stamp – > JavaScript – Weekly – useful – CN