Rust native vs WebAssembly execution speed— a comparison experiment using a fluid dynamics vortex particle simulation
The views/opinions expressed in this story are my own. This story relates my personal experience and choices, and is provided for information in the hope that it will be useful but without any warranty.
This story compares WebAssembly and native execution speeds in the context of Fluid Dynamics, a computationally intensive task to predict the movement of fluids. It investigates whether a fluid dynamics simulation could be undertaken using WebAssembly in a web-browser with little execution speed penalty over native execution.
This story will be relevant to people interested in the execution speed that can be achieved in a web-browser. In particular it provides a comparison of the performance of a computationally intensive application delivered as a native desktop executable and a WebAssembly module. This comparison can be used to inform the decision process when selecting software stack and software architecture for computationally intensive applications.
Background
This story expands on the description of the simulation engine described in the story Fluid Simulation Using Vortex Particle. When I developed it, I chose Rust for two reasons. The first reason is that Rust appears to be a promising language in regards to execution speed and support for multi-threaded applications. The second reason is Rust support for WebAssembly that is prominent on the Rust website. The wasm-bindgen and associated tooling supporting the compilation of Rust to WebAssembly makes its integration into a web page a reasonably easy task.
This story took longer to write than I expected as I got sidetracked into developing a visualisation for the simulation engine — with unsatisfactory results so far. Sorry...
Implementation
The work presented here is available in the following github repository. A live deployment of the WebAssembly version is available at cfd-webassembly.com — one needs to open the Developer Tool console to see the analysis running.
The fluid simulation software employed here is written in Rust and has been presented in this story. The same source code can be compiled as a native desktop executable and as a WebAssembly module that can be loaded in a web application.
The repository — at the time of writing — is split into the following directories:
rust
contains the Rust source code that is itself subdivided into 3 directories:vortex-particle-simulation
contains the simulation engine itself,cli
contains the source for the command line interface layer to the simulation engine andwasm
the interface layer for the WebAssembly;www
contains the source code for the web-page employed to test the WebAssembly module. The web-page uses VueJS and will be described in a subsequent story. The WebAssembly module is compiled in production mode even when served locally.
Baseline Performance
The baseline performance is measured using the release
optimisation setup for the native executable and production
optimisation setup for the WebAssembly executable. The comparison is based on the wall time per iteration, averaged over typically 100 iterations — in command line mode the number of iteration is nominated, whilst in the web-browser the solver is manually stopped after circa 100 iterations.
For replication purposes, the analysis is undertaken for a target number of vortons of 1,000 — which turns out to equate to 1,304 simulated vortons. The benchmark is undertaken on a Dell Latitude E7270 with an Intel Core i3–6100 at 2.30GHz.
The following options are compared:
- Native — in which the native executable is executed from the command line using
.\target\release\cli.exe --init ..\examples\vortex-ring.json -r
. The calculation time is reported to the console at then end of the simulation. - Chrome /Firefox/Edge— in which the WebAssembly is executed in the Chrome, Firefox or Edge web-browser respectively. The WebAssembly calculation reports the calculation time in the browser Developer Tool console when the simulation is stopped. The browser console can be shown by either pressing
F12
or displaying the developer tools in the web-browser — Chrome: click on the three dots, “More tools” and “Developer tools”; Firefox: click on the “burger” symbol [the three horizontal lines on the top-right], “Web Developer” and “Web Developer Tools”.
The analysis is repeated three times in each cases with the results averaged over the three runs. The results are presented in the figure below.
The comparison indicates two trends. Firstly, the execution speed of the native executable is significantly faster than in WebAssembly. Secondly, there are significant differences in execution speed between different web-browser. Firefox is significantly faster than Chrome and Edge but still takes more than twice as much time per iteration as the native executable.
Chrome and Edge execution speed are adversely affected when the developer console is open as is the case in this benchmark. When the developer console is closed, Chrome and Edge execution speed is on-par or slightly better than Firefox.
Performance Optimisation
I looked into options for optimising the performance through compiler flags. In particular the blog post Increase Rust and WebAssembly performance suggest altering the link-time optimisation flag and optimisation level, but these did not appear to affect the execution speed in my tests. I have not investigated or found any optimisation option to-date. If you are aware of an option to try, please do not hesitate to contact me.
I also compared the execution of the native and WebAssembly executable when compiled in debug
mode. The same trends were observed whereby execution was faster for the native executable, slower in Firefox and significantly slower in Chrome and Edge.
Conclusion
In this experiment , the execution speed of the fluid dynamic software compiled to WebAssembly is significantly slower than its execution speed when executed natively. In the particular setup employer, one time-step takes twice as much time in WebAssembly over native execution. This result is showing the same trend as observed in Not So Fast: Analyzing the Performance of WebAssembly vs. Native Code that reports WebAssembly running about 50% slower than native.
This study does not consider the benefit of WebAssembly over JavaScript, does not attempt to optimise the simulation engine and does not investigate different tool stack (such as emscripten) and hence the conclusions and figures presented herein needs to be considered in context. Furthermore, WebAssembly remains a relatively recent approach and hopefully further gains will be seen in the future.
I would be keen to hear of others’ experiences on the topic and investigate any suggestions for improvement of WebAssembly execution speed
Edit 16th January 2022: In the benchmark above, Chrome’s execution speed is affected by whether the developer console is visible or hidden. The reported speed have been evaluated with the developer console visible — which results in a slower execution speed. When the developer console is not visible, Chrome’s execution speed is actually faster than Firefox.