头图

Original: JavaScript engines and Just-In-Time compilation: A beginner's exploration, part 1

The JavaScript engine itself is also a piece of software that converts your gorgeous lines of JavaScript code into binary code executable by our machine.

All major browsers have developed their own JavaScript engines. Chrome has V8, Firefox runs SpiderMonkey (the first evolution of the JavaScript engine, developed by Brendan Eich for Netscape Navigator in the 1990s), Microsoft Edge has Chakra, and Safari has Nitro. Node.js is built on top of Chrome's V8 engine. IoT devices can also have a JavaScript engine.

Each JavaScript engine is responsible for using the ECMAScript rules and standards established by Ecma International's TC39.

Why modern JavaScript engines do Just-In-Time compilation

JavaScript is a dynamically typed language.

let x = 8
let y = "Henlo fren"

This means that whenever you declare a variable in JavaScript, you don't have to specify the type of information stored in the variable x. The JavaScript engine checks the type when executing the source code.

When declaring a variable in a statically typed language (such as C++), you must explicitly specify the type of the variable value.

int x = 8 
string y = "Henlo fren"

With such strict rules, statically typed languages can have a higher learning curve. Before trying to write a simple program, you must learn more about its rules and types.

However, from the perspective of the compiler, statically typed languages allow for faster performance. In advance, when the compiler began to convert the code into an executable machine code binary, the language provided the compiler with a lot of information about the source code.

On the other hand, dynamically typed languages like JavaScript rarely provide compilers with information about their types. This creates another layer of work for the compiler before generating machine code, making it slower than statically written language compilation.

But fear not, this is where Just-In-Time compilation comes in!

When JavaScript was first developed, it was designed to write a small amount of scripts to enhance web pages. As developers begin to build and use more JavaScript frameworks and libraries, and make AJAX requests, the demand for better and faster performance continues to grow.

When Chrome was launched in 2008, Google also released its V8 engine for the first time, which was the first of modern JavaScript engines. One of the main features of V8 is Just-In-Time compilation.

In Ahead-of-Time compilation, the compilation process must be completed before the system runs the executable machine code. With the new feature of Just-In-Time compilation, the V8 engine will compile the source code as needed, collect type information when executing the machine code generated during the compilation process, and then recompile the source code based on the information collected during the execution process. The back and forth between the two processes speeds up the performance of the execution process.

In order to allow JavaScript to run at the fastest speed even with dynamic typing, the JavaScript engine has some clever tricks.

Like most modern JavaScript engines, V8 has two compilers: a baseline compiler and an optimizing compiler.

When V8 compiles your JavaScript code, its parser generates something called an abstract syntax tree. Ignition, the baseline compiler or interpreter of V8, generates bytecode from this syntax tree. Ignition is faithful to its just-in-time compilation feature. It compiles JavaScript code, runs it, compiles it, runs it, back and forth, over and over again.

At runtime, the bytecode is analyzed, the engine recognizes the part that can be recompiled to obtain the best performance ("hot function"), and sends the code to TurboFan, which is an optimizing compiler for V8. It is just because of just-in-time compilation that the engine can recognize these so-called "hot functions" because of just-in-time compilation.

The + operator and V8 optimization

In her excellent lecture JavaScript Engine , V8 engineer Franziska Hinkelmann used the + operator to explain how V8 optimization works.

At first glance, the addition operator may seem simple, and any compiler can compile and execute it. However, if you look at the Ecma specification, before the program actually adds anything, the engine actually needs to perform a lot of steps:

Each of these steps is calling other functions, and these functions may call other functions, and so on. All engines must follow these Ecma specifications, so JavaScript is more than lawless.

Therefore, when your program has a function that adds two integers, when you call the function for the first time, the JavaScript engine will struggle through each of these steps, and finally add your two integers . When it goes through the JIT process (compile, run, compile, run, compile, etc.), it realizes that your function is hot, hot, and hot because you are calling it all the time. From the information collected by the engine at runtime, it also realizes that the data type used by this particular function is just an integer. With this information, V8 sends your code to TurboFan, its optimizer compiler, which generates better machine code for your function. Next time you call the function again, it will skip the lengthy Ecma step and your function will run faster.

But what happens when you decide to concatenate some strings when calling the function instead of adding two integers? V8 throws the function to the deoptimizer, sends it back to Ignition, and then Igntion executes the steps specified by Ecma again to run the function.

More original articles by Jerry, all in: "Wang Zixi":


注销
1k 声望1.6k 粉丝

invalid