Home>Article>Web Front-end> Understand how JavaScript works, dive into the V8 engine and write optimized code
Related free learning recommendations:javascript( Video)
A JavaScript engine is a program or interpreter that executes JavaScript code. A JavaScript engine can be implemented as a standard interpreter, or as some form of just-in-time compiler that compiles JavaScript into bytecode.
List of popular projects that implement JavaScript engines:
The V8 engine built by Google is open source and written in c. This engine is used in Google Chrome, however, unlike other engines V8 is also used in the popular Node.js.
V8 was originally designed to improve the performance of JavaScript execution in web browsers. To gain speed, V8 converts JavaScript code into more efficient machine code instead of using an interpreter. It compiles JavaScript code into execution-time machine code by implementing a JIT (Just-In-Time) compiler, just like many modern JavaScript engines such as SpiderMonkey or Rhino (Mozilla) do. The main difference here is that V8 does not generate bytecode or any intermediate code.
Before the 5.9 version of V8 came out, the V8 engine used two compilers:
The V8 engine also uses multiple threads internally:
When JavaScript code is executed for the first time, V8 uses the full-codegen compiler to directly translate the parsed JavaScript into machine code without any Convert. This allows it to start executing machine code very quickly. Note that V8 does not use intermediate bytecode, eliminating the need for an interpreter.
When the code has been running for a while, the analysis thread has collected enough data to determine which method should be optimized.
Next, Crankshaft starts optimization from another thread. It converts JavaScript abstract syntax trees into a high-level static single allocation (SSA) representation called Hydrogen and attempts to optimize the Hydrogen graph, most optimizations are done at this level.
The first optimization is to inline as much code as possible ahead of time. Inlining is the process of replacing the call site (the line of code that calls the function) with the body of the called function. This simple step allows the following optimizations to make more sense.
JavaScript is a prototype-based language: classes and objects are not created using a cloning process. JavaScript is also a dynamic programming language, which means that properties can be easily added or removed from an object after instantiation.
Most JavaScript interpreters use a dictionary-like structure (based on a hash function) to store the location in memory of object property values. This structure makes retrieving property values in JavaScript faster than in Java or C# Computational costs are higher in non-dynamic programming languages.
In Java, all object properties are determined by a fixed object layout before compilation, and cannot be added or removed dynamically at runtime (of course, C# has dynamic typing, which is another topic).
Thus, attribute values (or pointers to these attributes) can be stored in memory as contiguous buffers, with fixed offsets between each buffer. The length of the offset can be easily determined based on the attribute type, while in You can change property types at runtime which is not possible in JavaScript.
Because using a dictionary to find the location of an object's properties in memory is very inefficient, V8 uses a different approach: hidden classes. Hidden classes work similarly to fixed objects (classes) used in languages like Java, except that they are created at runtime. Now, let's look at their actual example:
Once the "new Point(1,2)" call occurs, V8 will create a hidden named "C0" kind.
No properties have been defined for Point, so "C0" is empty.
Once the first statement "this.x = x" is executed (within the "Point" function), V8 will create a second hidden class named "C1", which is based on "C0" . "C1" describes the location in memory (relative to the object pointer) where property x can be found.
In this case, "x" is stored at offset 0, which means when considering the point object in memory as a contiguous buffer, the first offset will correspond to the attribute "x" . V8 will also update "C0" with a "class transformation" which states that if the attribute "x" is added to the point object, the hidden class should switch from "C0" to "C1". The hidden class of the point object below is now "C1".
#Every time a new property is added to an object, the old hidden class is updated with a transformation path pointing to the new hidden class. Hidden class casts are important because they allow hidden classes to be shared between objects created in the same way. If two objects share a hidden class and the same property is added to them, the transformation will ensure that both objects receive the same new hidden class and all the optimization code that comes with it.
The same process is repeated when the statement "this.y = y" is executed (inside the "Point" function, after the "this.x = x" statement).
A new hidden class named "C2" will be created. If a property "y" is added to a Point object (which already contains the property "x"), a class transformation will be added to "C1" , then the hidden class should be changed to "C2", and the hidden class of the point object should be updated to "C2".
Hidden class conversion depends on the order in which properties are added to the object. Take a look at the following code snippet:
Now, assume that for p1 and p2, the same hidden class and transformation will be used. So, for "p1", first add attribute "a", then add attribute "b". However, "p2" is assigned "b" first, then "a". Therefore, "p1" and "p2" end up with different hidden categories due to different transformation paths. In this case it is much better to initialize the dynamic properties in the same order so that the hidden class can be reused.
V8 utilizes another technique for optimizing dynamically typed languages called inline caching. Inline caching relies on the observation that repeated calls to the same method tend to occur on the same type of object. An in-depth explanation of inline caching can be found here.
The general concept of inline caching will be discussed next (if you don’t have time to go through the in-depth understanding above).
So how does it work? V8 maintains a cache of object types passed as arguments in recent method calls and uses this information to predict object types passed as arguments in the future. If V8 could predict the type of object passed to a method well enough, it could bypass the process of how to access the object's properties and instead use stored information from previous lookups to the object's hidden class.
So how are the concepts of hidden classes and inline caching related? Whenever a method is called on a specific object, the V8 engine must perform a lookup of that object's hidden class to determine the offset at which to access the specific property. After two successful calls to the same hidden class, V8 omits the lookup of the hidden class and simply adds the property's offset to the object pointer itself. For all next calls to this method, the V8 engine assumes that the hidden class has not changed and jumps directly to the memory address of the specific property using the offset stored from the previous lookup. This greatly improves execution speed.
Inline caching is also why it is important for objects of the same type to share hidden classes. If you create two objects of the same type and different hidden classes (as we did in our previous example), V8 will not be able to use inline caching because even though the two objects are of the same type, their corresponding hidden classes are Its properties are assigned different offsets.
The two objects are basically the same, but the "a" and "b" properties are created in a different order.
Once the Hydrogen graph is optimized, Crankshaft reduces it to a lower-level representation called Lithium. Most Lithium implementations are architecture specific. Register allocation often occurs at this level.
Finally, Lithium is compiled into machine code. Then there is OSR: on-stack replacement. Before we start compiling and optimizing an explicit long-running method, we might run stack replacement. V8 doesn't just slowly perform stack replacement and start optimizing again. Instead, it converts all the context we have (stack, registers) to switch to the optimized version during execution. This is a very complex task, considering that among other optimizations, V8 initially inlines the code. The V8 isn't the only engine that can do it.
There is a safety measure called deoptimization that does the opposite conversion and returns unoptimized code assuming the engine is invalid.
For garbage collection, V8 uses the traditional mark-and-sweep algorithm to clean up the old generation. The marking phase should stop JavaScript execution. To control GC costs and make execution more stable, V8 uses incremental marking: instead of walking the entire heap and trying to mark every possible object, it just walks a portion of the heap and then resumes normal execution. The next GC stop will continue where the previous heap walk left off, which allows for a very brief pause during normal execution, as previously mentioned the scan phase is handled by a separate thread.
With the release of V8 5.9 earlier in 2017, a new execution pipeline was introduced. This new pipeline enables greater performance gains and significant memory savings in real JavaScript applications.
The new execution flow is built on Ignition (V8's interpreter) and TurboFan (V8's latest optimizing compiler).
Since the release of V8 5.9, as the V8 team has struggled to keep up with new JavaScript language features and the optimizations these features require, the V8 team has no longer used full-codegen and Crankshaft (since 2010) Serviced by V8 Technology).
This means that V8 will have a simpler and more maintainable architecture overall.
These improvements are just the beginning. The new Ignition and TurboFan pipelines pave the way for further optimizations that will improve JavaScript performance and shrink V8’s footprint in Chrome and Node.js for years to come.
Related free learning recommendations:php programming(video)
The above is the detailed content of Understand how JavaScript works, dive into the V8 engine and write optimized code. For more information, please follow other related articles on the PHP Chinese website!