Why was the V8 Engine Created?
But Is V8 the Best Engine Available Today?
For example, if you are building your own web browser it means you have plenty of memory, then V8 would be a great choice. But let's say you are making a small wearable IoT (internet of things) device then you will have very little memory available. In that case, you may use Duktape or Jerryscript, which may be slower but fit better in memory.
The V8 engine is written in C++ and has the following threads running internally:
- There is a main thread that loads, compiles, and runs the JS code
- Another thread is used for optimization and compilation, so the main thread continues to execute while the first thread optimizes the running code.
- The third thread is only used for feedback, telling the runtime which methods need further optimization.
- Few other threads for handling garbage collection.
Preparing the Source Code
Technically, it's not part of the V8's job. It is the renderer process of the browser that initializes the following two items:
- Host environment
- V8 engine
Every browser has several rendering processes. Typically, each browser tab has a render process and initializes a V8 instance.
In our context, the host environment is the browser. Therefore, we will use the “browser” and the “host environment” interchangeably in this article.
V8 engine first downloads the source code via a network, cache, or service worker. Once the code is downloaded, it is changed in a way that the compiler can understand. This process of changing the downloaded code so that compiler can understand it is called parsing and it consists of two parts: the scanner and the parser.
The parser encounters a script tag with a source. The source code inside this script is read as a UTF-16 byte stream into the byte stream decoder. This byte stream decoder then decodes the bytes into a token that is sent to the parser. To increase efficiency, the engine tries to avoid analyzing code that is not necessary right away.
Why Do We Need a Host Environment?
- Call stack
- Callback queue
- Event loop
- Web API and Web DOM
But why does the host environment stores the data in two different places?
- Trading space for speed: The call stack requires continuous space in memory to make the process faster but contiguous space in memory is rare. In order to solve this problem, browser developers limit the maximum size and this is the reason for the stack-overflow error. Browsers typically store a limited amount of data in the call stack, such as integers and other primary data types.
- Trading speed for space: The heap does not require contiguous space to store large data such as objects. The trade-off is that the heap is relatively slow to process data.
Compiling Code and Just-in-Time (JIT) Paradigm
At this step, the V8 engine creates nodes based on the token it receives and converts them into Abstract Syntax Tree (AST), and generates scopes.
How an Abstract Syntax Tree (AST) looks like?
For this, let's look at a simple example:
Similarly, each line of code will be converted into an AST.
Now, the most common way to convert code is to do precompilation i.e. the code is converted to machine code in the compilation stage before the execution of the program.
This approach is used by programming languages such as C++, Java, etc.
Precompilation allows all code to be evaluated together, resulting in better optimization and ultimately better-performing code. Interpreted implementations, on the other hand, are easier, but usually slower than a precompiled option.
So to work with dynamic languages more effectively, a new approach called Just-in-Time (JIT) compilation was developed which combines the best interpretation and precompilation techniques.
V8 uses an interpreter called Ignition which walks through the AST and generates byte code. An interpreter executes each line of bytecode from top to bottom. Once the bytecode is generated, the abstract syntax tree is deleted to free up memory space.
Now let's take an example and generate the bytecode for it manually:
V8 interpreter or ignition has a place where you can store and read values known as an accumulator. Accumulator eliminates the need to push and pop the top of the stack. It is also an implicit argument for many bytecodes and usually holds the result of the operation.
Execution of Machine Codes
In this step, ignition uses a table of controllers keyed by the bytecode to interpret instructions. For each bytecode, ignition can find the corresponding handler functions and execute them with the provided arguments.
As you run bytecode, V8 looks for opportunities to optimize your code.
When frequently used bytecodes are detected, V8 marks them as "hot". The hot code is then translated into efficient machine code and consumed by the Central Processing Unit (CPU).
What if optimization fails? The compiler breaks down the code and allows the interpreter to execute the original bytecode.
However, the V8 team introduces the bytecodes as the engine evolves. Why? Because using machine codes brings some difficulties.
1. Machine codes require a lot of memory
The V8 engine stored the compiled machine code in memory and reused it on page load.
Due to the reduced size, browsers can cache all the compiled bytecodes, skip all the previous steps and execute them directly.
2. Machine Code Is Not Always Faster than Bytecode
Bytecode takes less time to compile, but the trade-off is slower execution steps. The interpreter must interpret the bytecode before execution.
So which one is faster? It depends. The developer needs to balance the two options while creating a powerful bytecode interpreter and a smart optimizing compiler.
V8 interpreter, Ignition, is the fastest on the market.
The V8 optimization compiler is the famous turbofan that compiles highly optimized machine code from the bytecode.
3. Machine Codes Increase the Complexity of Development
Different CPUs can have different architectures. Each of them can understand only one type of machine language. There are many processor architecture designs in the market like ARM64, X64, S397, etc.
If the browser only used machine language, it would have to handle many things separately, therefore, an abstract (bytecode) is required.
The compiler works ahead of time and creates a translation of the written code and compiles it into a low-level machine-readable language.
The V8 engine uses a compiler known as Turbofan which takes bytecode from Ignition, inputs function feedback, applies a set of reductions based on it, and generates machine code.
But this doesn't guarantee that things won't change in the future.
For example, Turbofan optimized the code based on the assumption that some additions always add integers.
But what happens when you get a string? This process is called deoptimization. In such a case it returns to interpreted code, resumes execution, and updates the type of feedback.
What is Sandboxing?
Sandboxing refers to a method in which you use an isolated environment or "sandbox" for testing.
A sandbox is an environment for running software that is isolated and separated from other environments, even those on the same computer. In other words, a sandbox provides an isolated and safe environment to install and run a program, especially a suspect one without exposing the rest of your system and keeping your system protected from any zero-day threats of processes running in the sandbox environment.
Many developers use the V8 engine to build and deploy serverless scripting products for their websites.
V8 engine comes with WebAssembly support, which allows support for many programming languages.
Cold starts are a huge problem in serverless computing, but running a function on V8 means that your function can be "spun up" and run within few milliseconds.
Below is an example of a script deployed in the Serverless Script Engine Sandbox.
This script allows developers to identify the server from which the request was sent. Developers can use this information to provide region-specific services in real-time.
- Webkit: It is a browser engine developed by Apple and is primarily used by Safari, Mail, App Store, and many other apps on macOS, iOS, and Linux. WebKit was also used by BlackBerry Browser, PlayStation consoles from PS3 onwards, and browsers in Amazon Kindle e-readers.
- V8 uses an interpreter called Ignition which walks through the AST and generates byte code. An interpreter executes each line of bytecode from top to bottom.
- Abstract Syntax Tree (AST) is a way to represent the syntax of a programming language as a hierarchical, tree-like structure.
- V8 engine uses a compiler known as Turbofan which takes bytecode from Ignition, inputs function feedback, applies a set of reductions based on it, and generates machine code.
- Sandboxing refers to a method in which you use an isolated environment or "sandbox" for testing.