Transferred from: http://www.cnblogs.com/yumianhu/p/3707427.html
Reprint Please specify source: Http://blog.csdn.net/horkychen
The V8 JavaScript engine developed by Google is superior in performance. Let us familiarize ourselves with the author of the internal program implementation Yi Yuan code to see how V8 accelerates.
Community Engine Company Research and development engineer Hajime Morita
Google's V8 JavaScript engine in Chrome has attracted considerable attention due to its good performance. It was developed specifically for Chrome to run the Web application at high speed (WEBAPP). Chrome uses the Apple-led WebKit development program as the rendering Engine (Rendering). WebKit is also used in the Safari browser. WebKit's standard is equipped with a JavaScript engine called JavaScriptCore, but Chrome replaces it with V8 (Figure 1).
The V8 development team is a group of programming language experts. The core engineer, Lars Bak, developed the hotspot, an acceleration technology for Java Virtual Machines (VMS) developed by Sun Microsystems. He also developed an experimental Smalltalk system called Strongtalk in the United States, animorphic systems, which was acquired by Sun Microsystems in 1997. V8 fully developed the knowledge gained when developing hotspots and strongtalk.
Figure 1 Developing your own JavaScript engine Apple's safari and Google Chrome use the same rendering engine. The WebKit rendering engine with JavaScriptCore is standard in the JavaScript engine, but is replaced by V8 in Chrome.
demand for high-speed engines
Google's research team started developing V8 in 2006, in part because Google was dissatisfied with the speed with which the existing JavaScript engine was executing. I think the JavaScript engine was slow for two reasons: the historical context of development, and the complexity of the JavaScript language.
JavaScript exists for at least 10 years. In 1995, it appeared in the Web browser Netscape Navigator 2.0 developed by Netscape (Netscape Communications). However, there was a time when the performance requirement was not high because it was used only on a few animations, interactions, or other similar actions on the Web page. (The most obvious is to reduce network transmission to improve efficiency and improve interactivity!) The display speed of the browser depends on the speed of the network transmission as well as the rendering engine (rendering engines) parsing HTML, CSS (cascading style sheets, CSS), and other code. Browser development takes precedence over the speed of the rendering engine, and JavaScript processing speed is not too important. Java has made considerable progress at the same time, and it has been done more and more quickly in order to compete with C + +.
However, in the past few years, JavaScript has suddenly been widely used. The reason is that the software that was previously used as a desktop app, including the Office suite, is now a software that can be executed in the browser.
Google itself has launched several JavaScript web apps, including its Gmail email service, Google Maps map data service, and the Google Docs Office suite.
The speed of these applications is not only affected by the server, network, rendering engine, and many other factors, but also by the execution speed of the JavaScript itself. However, the existing JavaScript engine does not meet the new requirements, and poor performance has always been the most concern of Web application developers.
the question of the language itself
The JavaScript language specification is now under great pressure. For example, this is quite obvious when it determines the type of the variable. such as C + + and Java and other mainstream languages use static type (typing). The variable type can be declared when the code is compiled. Because you do not need to check the data types during execution, static types take on a performance advantage.
In general processing systems such as C + + and Java, the contents of fields* and methods* are stored in arrays, with 1:1 displacement (offset) corresponding to the names of fields and methods (Figure 2). The stored positions, such as individual variables and methods, are defined for each class. In languages such as C + + and Java, the types of variables (classes) that are accessed are known beforehand, so the language interpretation system (interpreting systems) uses arrays and displacements to access fields and method, and so on. Displacement makes it possible to access field, find field, or perform other tasks with just a few machine language instructions.
Figure 2 The differences between JavaScript and C + +, Java C + +, Java, and other processing systems store fields and methods, with their names in the array with the displacement values in the 1:1 corresponding array. The variable type (class) to be accessed is known in advance, so that fields and methods can be accessed using only arrays and displacements. In JavaScript, however, individual objects have tables of their own properties and methods. Each time a program accesses a property or a calling method, it must check the type of the object and perform the appropriate processing.
* Field: A variable of the genus object. Called member variables in C + +.
* Method: The processing type of the genus object. Called member functions in C + +.
* Property Attribute: The JavaScript property is a variable owned by the object itself. In JavaScript, attributes can be not only standard values, but also methods.
* Hash Table Hash Table: A data structure that returns corresponding values associated with a particular key. It has an internal array that uses the hash value produced by the key value (key) as the displacement of the list value for a particular position in the array. If a different key hash value is generated at exactly the same location, the inventory position stores multiple values, which means that the hash value must be checked for conformance before any value is returned.
On the other hand, JavaScript uses dynamic typing. The JavaScript variable has no type, and the type of the object specified is determined the first time it is executed (in other words, dynamically). Each time a property is accessed in JavaScript, or a method is sought, the type of the object must be checked and processed accordingly.
Many JavaScript engines use a hash table (hash table) to access properties and find methods. In other words, each time a property is accessed or a method is searched, the string is used as the key (key) to find the object hash table (Figure 3).
Figure 3 Internal JavaScript handling when property is accessed Use the string "foo" of the object X hash table as a keyword to search for "foo" content.
The Search hash table is a sequential action that contains the position within the array from the hash (hashing) value, and then see if the key value (key) for that position is typeface. It is then possible to use an array of displacements to directly read the data, which is more time-consuming to access.
Other languages that use dynamic types, as well as Smalltalk and Ruby. These languages are basically search hash tables, but they use classes to shorten the search time. However, JavaScript does not have classes. In addition to "numbers" indicates numeric values, "strings" is a string, and a few other types, other objects are of type "object". Programmers cannot declare types (classes) and therefore cannot use explicit types to speed up processing.
The flexibility of JavaScript allows you to add or delete properties and methods on an object at any time (see appendix). The JavaScript language is very dynamic, and the industry's general view is that dynamic languages are more difficult to accelerate than static languages such as C + + or java. Despite the difficulties, V8 uses several techniques to achieve accelerated goals:
1.JIT compilation (JIT Compile)
Generate machine language without bytecode (bytecode)
From a performance point of view, V8 has 4 main features. First, it generates machine language at execution time, called a just-in-time, JIT-based compilation method. This is a common way to improve the speed of interpretation, and this method can be found in languages such as Java and. Net. V8 has already practiced this technology earlier than the SpiderMonkey JavaScript engine in Firefox, or the competitive engine such as Safari's JavaScriptCore.
The V8 JIT compiler does not produce an intermediate code when generating machine language (Figure 4). For example, in the Java compiler, the original code is first converted to a class file represented by theVirtual intermediate language (called bytecode, bytecode). The Java compiler and bytecode compilers produce bytecode, not machine language. The Java VM interprets the bytecode sequentially in execution. This execution mode is called the bytecode interpreter (bytecode interpreter). Firefox's SpiderMonkey has an internal bytecode compiler and byte interpreter that converts the JavaScript source code into its own signature byte code for execution.
Figure 4 JIT compiler for V8 direct output of machine language the programming language system first uses the parser to convert the original code into an abstract syntax tree (abstract syntax tree, AST). There are several ways to deal with it before. The bytecode compiler compiles the abstract syntax tree into intermediate code and executes it in the compiler. Mixed mode such as Java JIT compiles part of this intermediate code into machine language to improve processing performance. Chrome does not use intermediate code, and JIT compiles the machine language directly from the abstract syntax tree. There are also abstract syntax tree interpreters that parse the abstract syntax tree directly.
In fact, Java VMS currently use a hotspot-based JIT compiler. It plays the role of the bytecode interpreter to parse the code, convert the frequently executed chunks of code into machine language and then execute, which is the mixed mode (hybrid model).
Bytecode interpreter, mixed mode, and so on, with the advantages of simple fabrication and excellent portability. As long as the original code that the engine can compile, the bytecode can be executed on any CPU architecture, which is why the technology is called a "virtual machine (VM)". Even in a mixed mode that produces machine code, it can be developed by the interpreter that writes the bytecode, and then the machine language generator is implemented. By using simple bit codes, it becomes much easier to optimize the output when machine code is generated.
Instead of converting the original program into an intermediate language, V8 directly produces the machine language and executes the abstract syntax. There is no virtual machine, and because there is no need for an intermediate representation, program processing begins earlier. On the other hand, however, it also loses the benefits of virtual machines, such as high portability (portability) and ease of optimization, such as byte-code interpreter and mixed mode.
2. Garbage Collection Management
A subtle implementation of Java standard features
The second key feature is that V8 the garbage collection Management (garbage collection, gc*) as "precise gc*". " Conversely, most JavaScript engines, Ruby, and other language compilers use Conservative gc* (Conservative GC) because conservative GC implementation is much simpler. Although the precise GC is more complex, it also has a performance advantage. The Oracle (Sun) Java VM is using the exact GC.
* Garbage collection (GC) garbage collection Management: automatically detects and frees memory space that is reserved by the program but is no longer in use.
* Conservative (Conservative) GC : The memory recovery management of the indicator and digital values is not strictly managed separately. This method is if it can be an indicator, then look at it as an indicator, even if it is possible to count the values. This method prevents an object from being accidentally reclaimed, but it also fails to release possible storage.
Although the exact GC itself is efficient, advanced algorithms based on accurate GC, such as generational (generational) GC, copy GC, and markup and thin processing (mark-and-compact processing), have significant performance improvements. Generational (generational) GC improves GC efficiency by separating the "young generational" objects (often collected) and the old generational (older generational) objects (objects that are relatively long-lived).
V8 uses a generational (generational) GC to replicate the GC with light (LIGHT-LOAD) on new generational (generational) processing, and to use markup and thin GC on the old GC, because it needs to move objects within the memory space. This is hard to do in a conservative GC. In the object's replication, compression (compaction) (called Defrag on hard disk) and similar actions, the address of the object changes, and for this reason, the most common way is to use "handle" (handles) to refer to the address indirectly. However, instead of using a handle (handles), V8 overrides all data referenced by the object. Not using handles (handles) makes the implementation more difficult, but it improves performance because fewer indirect references are available. The Java VM hotspot also uses the same technology.
3. Inline caching (inline cache)
Not available in JavaScript?
V8 can now produce the right machine language for x86 and arm architectures. While not using traditional optimizations in C + + or Java, V8 still has the inherent speed of dynamic languages.
One good example is the inline cache, a technique that avoids hash table searches when method calls and attribute accesses are available. It can cache the previous search results immediately, so it is called "inline". The technology has been known for some time and has been used in languages such as Smalltalk, Java, and Ruby.
Inline caching assumes that the object has a type, but not in the JavaScript language. Until V8 appears, and that's why the previous JavaScript engine didn't have an inline cache.
To break this limit, V8 parses the program operation at execution time and assigns a temporary class to the object using a "hidden class" (hidden classes). With hidden classes, even JavaScript can use inline caching. But these classes are a technique for improving execution speed, not an extension of the language specification. So they can't be referenced in JavaScript code.
4. Hide Class
Storage type conversion Information
Hidden classes pose interesting challenges for JavaScript language specifications that do not have a class, and are the most unique techniques V8 use to improve speed. They deserve more in-depth exploration.
There are two main reasons for building a class in V8, i.e. (1) categorizing objects with the same property names, and (2) identifying objects with different property names. Objects in the previous class have exactly the same object descriptions, which can speed up property access.
In V8, classes that match the collation criteria are configured on various JavaScript objects. The object references the configured class (Figure 5). However, these classes exist only in V8 as a convenience, so they are "hidden".
Figure 5 V8 object has a reference to a hidden class if the object's description is the same, the hidden class will be the same. In this example, both the object P and Q belong to the same hidden class
I mentioned above that you can add or remove attributes from JavaScript at any time. When this happens, however, the collation condition is destroyed (attributes with the same name are summed). The V8 is addressed by the new classes needed to establish the property changes. The object of the property change is included in the new level through a program called type conversion (class transition).
The second goal-identifying objects with different property names-is achieved by creating a new class. However, if you create a new class with each property change, you cannot continue to reach the first goal (the attribute with the same name is summed).
Figure 6 Configuring a new class: type conversion the object that the property is changing is classified as a new class. When Object P adds a new attribute z, the object P is classified as a new class.
V8 to resolve this problem by storing the transformed information inside the class. Consider Figure 7, which illustrates the scenario shown in Figure 6, when the hidden class point has the X and Y properties, the new attribute x is added to the point-level object p. When the new attribute z is added to the object p, V8 stores the "new attribute p, build Point2 class" information in a point-level internal table (Figure 7, Step 1).
Figure 7 Storing class transformation information in a class when a new attribute z is added to the object p, V8 records "Join attribute z on the table within the point class, establishing Class point2" (step 1). " When the object Q of the same point class joins the attribute Z, V8 searches the point class table first. If it finds out that the Point2 class has been added to attribute Z, the object q is set to the Point2 class (step 2).
When new attribute z is added to Object Q, which is also point level, V8 searches for a point-level table and discovers that the Point2 level has been added to attribute Z. When a class is found in a table, the object q is set to that class (Point2) without creating a new class (Figure 7, Step 2). This achieves the purpose of generalizing the object with the same name as the attribute.
However, this method means that the empty object corresponding to the hidden class will have a large conversion table. V8 is handled by creating hidden classes for each constructor. If the constructor is different, even if the object's statement (layout) is identical, a new hidden class is created for it.
inline caching (inline cache)
Other JavaScript engines, unlike V8, store object properties in a hash table, but V8 store them in an array. Displacement information-Specifies the position of an individual attribute in the array-is stored in the hash table of the hidden class. Objects of the same hidden class have the same property name. If you know the object class, you can manipulate the Access property by using the displacement array. This is much faster than searching the hash table.
However, in dynamic languages such as JavaScript, it is difficult to know the object type beforehand. For example, the original code for Figure 8 is the object type P and the Q call Lengthsquared () function. Object Types P and Q have different properties, and hidden classes are also different. Therefore, the parameter (arguments) type of the lengthsquared () function code cannot be determined.
To read the object properties in a function, you must first check the object's hidden class and have a hash table of the search class to find the displacement of the property. The array is then accessed using the displacement. Although accessing attributes in an array, the need to search the hash table first destroys the advantage of using arrays.
However, from different points of view, the situation is different. In a real-world program, there are not many cases where the dependency code performs the judging type. For example, the lengthsquared () function in Figure 8 even assumes that most of the values passed as parameters are point class objects, which is generally correct.
[JavaScript]View Plaincopyprint?
- function lengthsquared (p) {
- return p.x* p.x+ p.y* p.y;
- }
- function labeledlocation (name, x, y) {
- This.name= name;
- this.x= x;
- This.y= y;
- }
- var p= new Point (10, 20);
- var q= new labeledlocation ("Hello", 10, 20);
- var plen= lengthsquared (P);
- var qlen= lengthsquared (q);
Figure 8 Code sample: JavaScript cannot determine function parameter type It is not possible to determine whether the parameter is a point type or a labeledlocation type of the lengthsquared () function before execution.
Inline caching is an acceleration technique designed to take advantage of the local (local) category approach in the program. For programmatic property access, V8 generates a command string to search for a hidden class list (Figure 9). This code is called the premonomorphic stub. This stub is for the function Access property (Figure 10). The premonomorphic stub has two messages: hidden classes for search and displacement from hidden. Finally, a new code is generated to cache this information (Figure 11).
[JavaScript]View Plaincopyprint?
- object* find_x_for_p_premorphic (object* p) {
- class* klass= P->get_class ();
- int offset = Klass->lookup_offset ("x");
- Update_cache (Klass, offset);
- Return p->properties[offset];
- }
Figure 9 in the pseudo-code (pseudocode) premonomorphic stub Gets the attribute offset from the hidden class.
Figure ten premonomorphic stub the premonomorphic stub is called when a property in the call access function is called.
[JavaScript]View Plaincopyprint?
- object* find_x_for_p_monomorphic (object* p) {
- if (Cached_klass = = P->get_class ()) {
- Return p->properties[cached_offset];
- } else {
- Return Lookup_property_on_monomorphic (P, "X");
- }
- }
Figure 11 Pseudo-code monomorphic stub The displacement that is handled in the direct embed code is the constant used to access the property.
Hidden classes of objects with attributes are compared to cache-hidden classes before the table is searched. If the match does not need to search again, and can use the buffer displacement to access the property. If the hidden class does not match, the displacement is judged in a general way through the hidden class hash table.
The newly generated code is called the monomorphic stub. The word "inline" means that the displacement required to query the hidden class is embedded in the resulting code in an immediate, usable form. When the monomorphic stub is called for the first time, it rewrites the function from the first address called in the Pre-monomorphic stub address to the Monomorphic stub address (Figure 12). Since then, using a high-speed monomorphic stub, class comparison and array access can be used to handle property access.
Figure monomorphic Stub call when a monomorphic stub is called, it re-writes the function from the first address called in the premonomorphic stub address, to the monomorphic stub address.
If there is only one object with attributes, the monomorphic stub will be highly efficient. However, if the type is more, the cache error will be more frequent, thereby reducing the efficiency of the monomorphic stub.
When a cache error occurs, V8 is resolved by producing another code called the megamorphic stub (Figure 13). Monomorphic stubs corresponding to individual classes are written in a hash table, which is searched and called stubs at execution time. If there is no monomorphic stub for the type, the displacement is searched from the type hash table.
[JavaScript]View Plaincopyprint?
- object* find_x_for_p_megamorphic (object* p) {
- class* klass= P->get_class ();
- inline processing of actual searches
- stub* stub= klass->lookup_cached_stub ("x")
- if (NULL! = Stub) {
- Return (*stub) (p);
- } else {
- Return Lookup_property_on_megamorphic (P, "X");
- }
- }
Figure 13 megamorphic stub processing in pseudo-code the monomorphic stub corresponding to the type is stored in the hash table in advance and is searched and called out at execution time. If the corresponding monomorphic stub cannot be found, the displacement is searched in the type hash table.
When a cache error occurs on the monomorphic stub, the monomorphic stub overrides the first address that is called from the monomorphic stub address at the Megamorphic stub address. In terms of code search, the performance of the megamorphic stub is lower than the monomorphic stub, but the Megamorphic code is much faster than premonomorphic stubs with cache updates, code generation, and other ancillary processing.
Covering multiple kinds of inline caches is called multi-modal inline caching (polymorphic inline cache). The V8 embedded cache system is used to call methods and access properties.
characteristics of machine language
As mentioned above, V8 uses such as inline caching to achieve the natural speed of a dynamic language at design time. Create a machine language generation module that uses the stub of the inline cache to closely link with the JIT compiler.
Some frequently used methods are also written in machine languages to achieve the same effect as inline outreach, making them "intrinsic". The V8 source lists the candidate lists for intrinsic conversions.
V8 contains shell programs that can be used to check the machine language produced by V8. The resulting instruction string can be compared with the V8 code in order to show its characteristics.
For example, when executing the JavaScript function shown in Figure 14a, a x86 machine language instruction string, shown in 14b, is generated. This function is called in the 39th instruction and is a "n+one" addition. In JavaScript, the "+" operand indicates the addition of a numeric variable and the continuity of the string. The compiler does not generate code to determine which of these is the calling function to be responsible for judging.
Figure V8 Machine language generated from JavaScript code The addition process is converted to the machine language of the function call (A, B).
If the function of Figure 14 changes slightly (Figure 15), then the function call of Figure 14b disappears, but there will be an addition instruction (20th), and a branch instruction (jnz if not 0 jump, 31st). When an integer is used as the operand of the "+" operand, the V8 compiler produces a command string with an "add" instruction without calling the function. If the operand ("n") is found to be an indicator of number object or String object (pointer),
The function is called. "Addition" only occurs when the operand of the two "+" operation is an integer. In this case, execution is faster because a function call can be skipped.
Figure V8 The machine language generated from the JavaScript in Figure 14, modified slightly
Additionally, the 0x2 is added with an "addition" instruction because the least significant bit (least significant bit, LSB) is used to distinguish between integers (0) and indicators (1). Add 0x2 (10 in binary) just as if the value is plus 1,lsb. In the JO Command Overflow (overflow) processing, the test and JNZ instructions are used to determine the indicator and skip downstream processing (note 1).
This kind of trick is everywhere in the compiler. However, the generator code also reveals the compiler's limitations. Traditionally optimized compilers can produce exactly the same machine language for figures 14 and 15, due to the constant rounding relationship. However, the V8 compiler generates code in the abstract syntax tree unit, so there is no optimization when processing extends multiple nodes. This is also evident in the large number of push and pop instructions.
Figure 16 shows a reference to the same processing in the C language. Because of the different language specifications between C and JavaScript, the resulting machine language is different from Figure 14 and Figure 15, which is independent of the compiler's performance.
Figure C Compiler-generated machine language from C code The resulting machine language is much cleaner than V8 (A, b), mostly due to differences in C and JavaScript language specifications.
Note 1: When the overflow signal appears, the Jo command jumps to a specific address. The test instruction reflects the logical and result into 0 and symbol indicators. The jnz instruction jumps to a specific address unless the 0 signal appears.
* Abstract Syntax Tree: The data representing the schema of the program in the tree schema.
Appendix: References for programmers familiar with OOP
Also refer to: http://blog.csdn.net/horkychen/article/details/7559134
JavaScript has no classes, but to make it easier for programmers familiar with the use of classes (object-oriented code), you can use "new" operands to build objects, just like in Java. A special "constructor" constructor is defined after the "new" operand (figure B-1 A, b).
However, even without constructors, you can create objects (figure b-1c) and set properties (Figure B-1 D). The properties and methods of JavaScript objects can be added or deleted at any time.
In addition to using dot notation to access JavaScript properties, you can also use parentheses to suggest hashing (hashing) access (Figure B-1 E, f) or a variable-specific property name string (Figure B-1 g). The explicit display of JavaScript objects from these examples is designed to use a hash table.
[JavaScript]View Plaincopyprint?
- A) define the constructor "point"
- function point (x, y) {
- This refers to its own
- this.x= x;
- This.y= y;
- }
- B) objects created when new and called constructor functions are added
- var p= new Point (10, 20);
- C) object can also be created without a constructor function
- var p= {x:10, y:20};
- D) The freedom to add properties to the object
- p.z= 30;
- e) using dot tag access properties
- var y= p.y
- f) Access using the hash of parentheses (hashing)
- var y= p["Y"];
- g) You can also use variables for hashing (hashing) access
- var name= "Y";
- var p[name];
Figure B-1 JavaScript code example
Although this article was written in 2009 V8 just launched, which still has a great help to understand V8.
Original address: http://techon.nikkeibp.co.jp/article/HONSHI/20090106/163615/
Chinese Traditional version address: Http://www.greenpublishers.com/neat/200901/3coverstory.pdf
* This article is based on the traditional Chinese version of the revision. It seems that the traditional Chinese version of the manual correction after the machine, apart from the two sides of the professional vocabulary, there are still a lot of places. The most obvious is to translate the class into a hierarchy.
Why V8 engine so fast? Reproduced