Web Assembly Demystified
Web Assembly is an open standard instruction set for executable programs in the browser developed by a collaborative effort Between W3C, Mozilla, Google, Microsoft, Apple.
Introduction
“Web Assembly (abbreviated WASM) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications” - Web Assembly Website. This is the textbook definition of web assembly, could be hard to understand so let us simplify and break this down.
What is Web Assembly?
The Need: Browsers lack deep level hardware and IO integration that the operating system. Hence various web applications could not utilize the resources that native OS level Apps could.
Problem Faced: Native applications are built from languages with the ability to directly access and manipulate memory, such as C or C++, Javascript on its own lacked that ability.
Solution Developed: Using a concept called Virtual Machines, basically allowing OS kernel level access and resource sharing with an emulated operating system guest, which runs in the Browser Environment.
Web Assembly is an open standard instruction set for executable programs in the browser developed by a collaborative effort Between W3C, Mozilla, Google, Microsoft, Apple.
- Web Assembly emulates a POSIX operating system in the browser, which allows for programming languages such as C to access deep memory and hardware resources.
- Web Assembly runs on all major web browser engines: including Chrome (V8), Firefox (Gecko), Safari (Web Kit) and Apple (Web Kit).
- Web Assembly can also run on runtime environments like NodeJS, Deno and Bun.
Working
Basics of web assembly are as follows: Compiled: Current implementations of Web Assembly are compiled using compilers like emscripten. It follows Linear Memory: A WASM module has access to a single which is essentially a flat array of bytes. This memory can be grown by a multiple of the page size (64K). It cannot be shrunk. WASM File: WASM file is binary Machine code and unreadable, a more friendly WAT file can be read however.
The Question is: What is a WASM Compiler?
WASM is not a programming language it is an instruction set, binary code. Like assembly. Hence, we can use any programming language that has IO access and compile it to web assembly. Emscripten to compile C to WASM.
There are two types of WASM compilers, ahead of time (AOT) and just in time(JIT). WASM interpreters are being developed but have found no practical use.
Process of Running WASM in V8 Enviornment
Compilation: WASM File is compiled by a compiler, Lift Off
Optimization: Code is optimized further by TurboFan enhance performance.
Running: The Whole WASM file can be called and run, or individual functions can be streamed in JS file.
JS vs WASM
JS | WASM |
---|---|
JS is interpreted rather than compiled | All WASM code is compiled beforehand |
JS engine is not a virtual machine it is a runtime environment* | WASM runs all code in a virtual machine |
JS code optimization is not mandatory, instead it is an opt in - opt out model | Optimization is mandatory and will happen |
JS has access to DOM and a garbage collector for secure and safe code | JS has access to DOM and a garbage collector for secure and safe code |
*The V8 engine is not a virtual machine because it doesn’t simulate the workings of real computer hardware. It is more like a byte-code interpreter or JavaScript interpreter than anything that virtualizes hardware.
Pros and Cons
Pros
- Pre existing Code: allows us to use vast high performant code.
- Variety: Can use various languages.
- Performance: WASM applications perform more consistently across browsers and devices.
- Upgrades: Parallel processing and SIMD support will make WASM better.
Pre Existing Code:
A big advantage that JS has is that it has a vast library of packages and a deep ecosystem. Things such as npm or yarn. Which means you can easily find a library for any task you want.However, many languages can be compiled to WASM hence we can use the ecosystems of various languages. (E.g) for apps that require lots of video processing we can use FFMPEG to process video. Moreover, WASM and JS are not mutually exclusive, meaning we can use C++ or Rust libraries inside JavaScript functions, meaning we can enhance our existing apps without changing their codebase to Rust or C++
Performance:
First, let's clear a misconception, Web Assembly is not faster than Javascript. In its optimized state, JavaScript can even outperform Web Assembly. WASM performance is more consistent than JS, the reason for this is due to how JavaScript Engines work.
E.g. The V8 engine interpreter ignition first interprets line by line and observes the behavior of the code, then the TurboFan. Optimization is opt in, not compulsory unlike WASM.
Also it is much easier to write slower code in JavaScript due to nested higher order functions and so on.
Future of WASM
Parallel Processing: In the future parallel processing and “Single Instruction and Multiple Data Stream” (SIMD) will be simple to implement and be widely supported.
Ever Expanding: Due to it being and open source initiative, web assembly will be forever expansive. On the WASM official website there is a roadmap which one can view to see the changes.
Cons
No DOM: Hader to create good pages, needs JS.
Security: Easier to hide malware as source code is hidden.
No Garbage Collection: Potential memory leaks and no data recovery.
How To?
Getting started to incorporate web assembly in your projects.
Preliminary Information:
- Programming language used in this section is C++.
- Compiler used in this section is Emscripten.
- Editor used in this section is VSCode.
- JavaScript Runtime environment is Node.js version 16.
- Using the Chrome browser’s latest version.
- Operating system is Manjaro Linux.
First open your terminal and grab the latest clone of emscripten from github, you can do that by using
git clone https://github.com/emscripten-core/emsdk.git
Afterwards install the compiler
On Linux
Cd emsdk
./emsdk install latest
./emsdk activate latest <- Run both after every reboot
Source ./emsdk_env.sh <- this step is important otherwise you won’t be able to access emsdk compiler outside of the directory in compiler
After installation of the compiler you can write any code in C++
#include<iostream>
#include<emscripten.h>
EXTERN “C” {
EMSCRIPTEN_KEEP_ALIVE int myOwnCplusFunction(int a, int b) {
return a + b
}
}
int main () {
for (int I <0 ; I < 10; I++)
{
cout<<I<<endl
}
}
If any of you have programmed in C++ then you can see there are 3 inclusions different from regular code. Firstly a new header file, this allows us to access emscripten tags that we can later utilize to further tune our code.
Secondly the extern “C” is added to make sure that the compiler does not change the C++ function names, because emscripten can compile both C and C++ code we need this extern to differentiate between them to preserve function names,
Lastly the EMSCRIPTEN_KEEP_ALIVE
tag is there to persist the function in the compiler to make sure we can reuse that in our JavaScript code as well.
In order to compile we go to our terminal and type emcc –o firstwasm.js firstwasm.cpp -o
We can specify the output of the glue code, this glue code is a shortcut to running web assembly in our run time environment or browser. Otherwise we will need to use the browser or run time environment own web assembly modules and manually set memory buffers to run it which is time consuming. The glue code can also be html which can directly run in the browser but we prefer generating a .js file because it can run in Node.js.
Lastly to persist our functions so we can re call them whenever we want, we export some runtime methods ccall
and cwrap
.
emcc –o firstwasm.js firstwasm.cpp –s EXTRA_EXPORTED_RUNTIME_METHODS= ‘[“ccall”, “cwrap”]
When we go to our javascript code:
import Module from “./firstwasm.js”
Module.onRuntimeInitialized = () => {
const result = ccall(“myOwnCplusFunction”, “number”, [“number,”number], [12, 2])
console.log(result)
const myCplusinJs = cwrap(“myOwnCplusFunction”, “number”, [“number,”number])
}
console.log(myCplusinJs(12, 2))
In this code we can see that both functions were exported during runtime and persisted in our virtual machine, hence we can call them directly. The difference between ccall and cwrap is that ccall calls the function once and stores the result in a variable. This is done during compile time and cwrap compiles the function and wraps it, so that the function can be stored in the variable. Cwrap is more useful as it allows us to use C++ functions as many times as we want in js.
And that is about it for the basic web assembly programming knowledge. There is also something called AssemblyScript which is a delight for JavaScript and TypeScript developers.
Assembly Script is a language that follows the typescript syntax however, it compiles to WebAsembly. AS is useful when we need our application to utilize parallel processing or simd however=, it would be wasteful to learn a completely new programming language for it.
A sample Assembly Script program is
class ExtendedClass extends BaseClass {
extendedProp: i32;
constructor(extendedValue: i32) {
super(1);
this.extendedProp = extendedValue;
}
add(a: i32): i32 {
return super.add(a, this.extendedProp + super.instanceProp);
}
}
export function getStaticProp(): i32 {
return ExtendedClass.staticProp;
}
export function overloadAdd(value: i32): i32 {
let extendedClass = new ExtendedClass(value);
return extendedClass.add(24);
}
The difference is subtle, firstly there are new data types in AS such as i32
which indicates an integer of 32 bits. These types are important as AS interacts directly more closely with memory.
Summary
Web Assembly is a virtual machine on web that can run other languages. It works by compiling native language into WASM and optimizing it. It should be used because it results in consistent performance and allows more access to the code. And it is pretty easy to integrate as you just have to download the proper compiler, set up your environment and go!
Recommendations
- Recognize the need: Before thinking of implementing we must check needs.
- Collect Requirements: Check if any WASM and selected library works.
- Analyze the Program: Make sure there is no disruption in the process.
- Clean Architecture: Make sure to maintain clean architecture after WASM
- Strive to Learn: If we can learn various JS frameworks, we can learn new languages too.
- JS♥️WASM: JS and WASM are friends not rivals. Use them together to get best results.
What are your thoughts? You are open to ask any Question and leave a comment. Thanks