How we using WebAssembly 20 times accelerated web application
WebAssembly - what is it?
It is important that WebAssembly can be obtained by compiling sources in languages such as C / C ++, Rust, Go. It uses static typing and the so-called flat memory model. The code, as mentioned above, is stored in a compact binary format, so it runs almost as fast as if the application was launched using the command line. These features have led to the growing popularity of WebAssembly.
We remind you: for all readers of “Habr” - a discount of 10,000 rubles when registering for any Skillbox course using the “Habr” promo code.
Skillbox recommends: Practical course "Mobile Developer PRO" .
Wasm is currently used in many applications, from games like Doom 3 to ported to web applications like Autocad and Figma. Wasm is also used in areas such as serverless computing.
This article provides an example of using Wasm to speed up an analytic web service. For clarity, we took a working application written in C, which is compiled into WebAssembly. The result will be used to replace low-productivity sections of JS.
The example will use the browser service fastq.bio, which is intended for geneticists. The tool allows you to evaluate the quality of DNA sequencing (decoding).
Here is an example of an application in work: The
details of the process should not be given, since they are rather complicated for non-specialists, but in short, the scientists from the above infographics can understand whether the DNA sequencing process went smoothly and what problems arose.
This service has alternatives, desktop programs. But fastq.bio can speed things up by visualizing data. In most other cases, you need to be able to work with the command line, but not all geneticists have the necessary experience.
Everything works simply. At the input - data presented in the form of a text file. This file is generated by specialized sequencing tools. The file contains a list of DNA sequences and a quality assessment for each nucleotide. The file format is .fastq, which is why the service got its name.
After calculating the necessary indicators, they are visualized using Plotly.js, and the service starts working with a new data selection. Fragmentation is done to improve the quality of UX. If you work with all the data at once, the process will freeze for a while, since files with sequencing results occupy hundreds of gigabytes of file space. The service takes pieces of data from 0.5 to 1 Mb in size and works with them step by step, building graphic data.
Here's how it works:
The red box contains the string conversion algorithm for rendering. This is the most loaded part of the service in terms of computing. It is worth trying to replace it with Wasm.
To assess the possibility of using Wasm, the project team began searching for ready-made solutions for creating QC-metrics (QC - quality control) based on fastq files. The search was carried out among the tools written in C, C ++ or Rust, so that it was possible to port the code to WebAssembly. In addition, the tool should not be "raw", it required a service already verified by scientists.
As a result, the choice was made in favor of seqtk . The application is quite popular, it is open-source, the source language is C.
Before converting to Wasm, you should look at the principle of compiling seqtk for the desktop. According to the Makefile, here is what you need:
# Compile to binary $ gcc seqtk.c \ -o seqtk \ -O2 \ -lm \ -lz
Basically, you can compile seqtk with Emscripten. If it’s not there, get around the Docker image .
$ docker pull robertaboukhalil/emsdk:1.38.26 $ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26
If you wish, you can collect it yourself , but it takes time.
Inside the container, you can easily take emcc as an alternative to gcc:
# Compile to WebAssembly $ emcc seqtk.c \ -o seqtk.js \ -O2 \ -lm \ -s USE_ZLIB=1 \ -s FORCE_FILESYSTEM=1
Instead of outputting to the Emscripten binary file, .wasm and .js are used to generate the files, which is used to launch the WebAssemby module.
To support the zlib library, the USE_ZLIB flag is used. The library is distributed and ported to WebAssembly, and Emscripten includes it in the project.
The Emscrippten virtual file system is activated. This is a POSIX-like FS operating in RAM inside the browser. When the page refreshes, the memory is cleared.
To understand why a virtual file system is needed, it is worth comparing the way to start seqtk from the command line with the way to run the compiled WebAssembly module.
# On the command line $ ./seqtk fqchk data.fastq # In the browser console > Module.callMain(["fqchk", "data.fastq"])
Gaining access to the virtual file system is necessary so as not to overwrite seqtk with string rather than file input. In this case, the data fragment is displayed as the data.fastq file in the virtual FS with the main () seqtk called on it.
Here's the new architecture: The
figure demonstrates that instead of computing, the main browser stream uses WebWorkers . This method makes it possible to perform calculations in the background thread without affecting the responsiveness of the browser. Well, the WebWorker controller launches Worker, controlling its interaction with the main thread.
The seqtk command is launched using Worker on the mounted file. After completion of the work, Worker displays the result in the form of Promise. When a message is received by the main thread, the result is used to update the schedules. And so in a few iterations.
What about WebAssembly performance?
When using the out-of-the-box solution, the performance gain was nine times.
This is an excellent result, but as it turned out, there is an opportunity to optimize it. The fact is that a large number of QC analysis results are not used by seqtk, so you can delete them. If you do this, the result compared to JS is improved 13 times.
It was possible to achieve it by simply commenting on the printf () commands.
But that is not all. The fact is that at this stage fastq.bio receives the results of the analysis by calling different functions of C. Any of them calculates its own set of characteristics, so that each fragment of the file is read twice.
In order to get around this problem, it was decided to combine the two functions into one. As a result, productivity increased by 20 times.
It is worth noting that such an outstanding result can not always be achieved. In some cases, performance drops, so it is worth evaluating each case.
As a conclusion, we can say that Wasm really makes it possible to improve application performance, but you need to use it wisely.