For more details on the browser, see 47 images that take you inside the browser world

V8 is an open source JavaScript engine developed by Google, also known as a virtual machine, which simulates the functions of a real computer to compile and execute code.

Clear version of the picture above

Remember that year under the flower, late at night, first know Xie Niang

Why do you need a JavaScript engine

When we write JavaScript code directly to the browser or Node for execution, the underlying CPU is unaware of it and cannot execute it. The CPU only knows its own instruction set, which corresponds to assembly code. Writing assembly code is a pain. And different types of cpus have different instruction sets, which means you have to rewrite assembly code for each one. JavaScirpt engine can compile JS code into assembly code corresponding to different CPUS (Intel, ARM, MIPS, etc.), so we don’t need to go through the instruction set manual of each CPU to write assembly code. Of course, the JavaScript engine doesn’t just compile the code, it also executes the code, allocates memory, and collects garbage.

Mov ax,bx # assembly instructionCopy the code

Data to expand: assembly language introductory tutorial | nguyen half-stretching 】 【 understand V8 bytecode “translation”

Hot JavaScript Engines

  • V8 (Google), written in C++, open source, developed by Google Denmark, is part of Google Chrome and is also used in node.js.
  • JavaScriptCore (Apple), open source for WebKit-based browsers such as Safari, implemented the compiler and bytecode interpreter in 2008 and upgraded to SquirrelFish. Apple’s internal JavaScript engine code-named “Nitro” is also based on the JavaScriptCore engine.
  • Rhino, managed by the Mozilla Foundation, is open source, written entirely in Java, and used for HTMLUnit
  • SpiderMonkey (Mozilla), the first JavaScript engine, originally used by Netscape Navigator and now used by Mozilla Firefox.
  • Chakra (JScript engine), for Internet Explorer.
  • Chakra (JavaScript engine) for Microsoft Edge.
  • KJS, KDE’s ECMAScript/JavaScript engine, was originally developed by Harry Bolton for use in the KDE project’s Konqueror web browser.
  • JerryScript – Samsung’s small JavaScript engine for embedded devices.
  • Others: Nashorn, QuickJS, Hermes

V8

Google’S V8 engine is an open source high-performance JavaScript and WebAssembly engine written in C ++, which has been used in Chrome and Node.js, among others. Can run on Windows 7+, macOS 10.12+ and Linux systems using X64, IA-32, ARM or MIPS processors. V8 was first developed to be embedded in Google’s open source browser, Chrome. The first version was released with the first version of Chrome on September 2, 2008. But V8 is a module that can run on its own and can be embedded in any C ++ application. The well-known Node.js(an asynchronous server framework that allows you to write efficient web servers using JavaScript on the server side) is based on V8, and Couchbase and MongoDB also use V8.

Like other JavaScript engines, V8 compiles/executes JavaScript code, manages memory, is responsible for garbage collection, interaction with the host language, and so on. By exposing the host object (variables, functions, etc.) to JavaScript, JavaScript can access the objects in the host environment and perform operations on the host object in the script.

Information expansion: v8 logo (JavaScript engine) | | v8 “v8, the present and future of JavaScript +” | a few picture to let you understand WebAssembly

The first acquaintance with you is like the return of an old friend

What is the D8

D8 is a very useful debugging tool, which you can think of as an acronym for Debug for V8. We can use D8 to see various intermediate data in V8’s execution of JavaScript, such as scope, AST, bytecode, optimized binaries, garbage collection status, and some internal information using the private API provided by D8.

The executable program compiled from the V8 source code is called D8. D8 exists as an interactive shell that the V8 engine can use on the command line. Google officials don’t remember the origin of the name d8, but as an acronym for “Delveloper shell,” the combination of the initial d and 8 makes perfect sense. Another saying is that d8 was originally called the developer shell because d had 8 characters and so was abbreviated to D8, similar to shorthand like i18n(internationalization).

Using d8

Install the D8

  • Method 1: download and compile

    • V8 Google download and compile for use
    • Official document: Using D8
  • Method two: Use the compiled D8 tool

    • MAC
    • Linux32 platform
    • On the platform
    • Win32 platform
    • Win64 platform
    // Unzip the file and click d8 to open it (if the MAC security policy is limited, hold down control, click again, and select Open from the popup menu)
      V8 version 8.4109.
      d8> 1 + 2
        3
      d8> 2 + '4'
        "24"
      d8> console.log(23)
        23
        undefined
      d8> var a = 1
        undefined
      d8> a + 2
        3
      d8> this
        [object global]
      d8>
    Copy the code

    The file directory structure used in demo later in this article:

    V8:# d8 executable file
        d8
        icudtl.dat
        libc++.dylib
        libchrome_zlib.dylib
        libicui18n.dylib
        libicuuc.dylib
        libv8.dylib
        libv8_debug_helper.dylib
        libv8_for_testing.dylib
        libv8_libbase.dylib
        libv8_libplatform.dylib
        obj
        snapshot_blob.bin
        v8_build_config.json
        # New js sample file
        test.js
    Copy the code
    • Method 3: MAC

        If HomeBrew already exists, ignore the first command
        ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
        brew install v8
      Copy the code
    • Method 4: Use node instead. For example, use node — print-bytecode. /test.js to print out the Bytecode generated by Ignition.

What d8 commands are available?

  • View the d8 command

      If you don't want to debug using./d8, you can add d8 to the environment variable and then 'd8 --help' directly
      ./d8 --help
    Copy the code
  • Filter specific commands

      If you are running Windows, the grep program may be missing, please download and install it by yourself and add environment variables
      ./d8 --help |grep print
    Copy the code

    Such as:

    • Print-bytecode Views the generated bytecode
    • Print-opt-code Displays the optimized code
    • Print-ast Displays the GENERATED ast
    • Print-scopes check the scope generated in the middle
    • Trace-gc looks at the memory reclamation status of this code
    • Trace-opt sees which code has been optimized
    • Trace-deopt sees which code has been de-optimized
    • Turbofan-stats prints some statistics for the optimized compiler

Debug using D8

// test.js
function sum(a) {
  var b = 6;
  return a + 6;
}
console.log(sum(3));
Copy the code
  # d8 is followed by the file name and the command to execute. If you execute the following command, the bytecode generated by the test.js file will be printed.
  ./d8 ./test.js --print-bytecode
  # Run the following command to output 9
  ./d8 ./test.js
Copy the code

Internal methods

You can also use some of the internal methods offered by V8 by passing the –allow-natives-syntax command when starting V8, You can use internal methods such as HasFastProperties (to check if an object HasFastProperties) in test.js (indexed properties, general properties, fast properties, etc.).

function Foo(property_num, element_num) {
  // Add indexable attributes
  for (let i = 0; i < element_num; i++) {
    this[i] = `element${i}`;
  }
  // Add general properties
  for (let i = 0; i < property_num; i++) {
    let ppt = `property${i}`;
    this[ppt] = ppt; }}var bar = new Foo(10.10);
// Check whether an object has a fast property
console.log(%HasFastProperties(bar));
delete bar.property2;
console.log(%HasFastProperties(bar));
Copy the code
  ./d8 --allow-natives-syntax ./test.js
  # Print: true false
Copy the code

Heart like double screen, there are thousands of knots

The internal structure of a V8 engine

V8 is a very complex project, with over a million lines of C++ code. It is made up of many submodules, of which these four are the most important:

  • Parser: Converts JavaScript source code to Abstract Syntax Tree (AST).

    To be exact, before “Parser” converts JavaScript source code into an AST, there is a process called “Scanner”, which follows:

  • Ignition: Interpreter: Converts the AST into a Bytecode, interprets and executes the Bytecode; Collect the information TurboFan needs to optimize compilation, such as the types of function parameters; There are four main modules in the interpreter execution: byte code in memory, register, stack and heap.

    There are usually two types of interpreters, stack-based and register-based, the stack based interpreter uses the stack to store function parameters, intermediate results, variables, etc. Regist-based virtual machines support instruction operation of registers and use registers to store parameters and intermediate calculation results. In general, stack-based VMS also define a small number of registers, and register-based VMS also have stacks, whichThe difference lies in the instruction set architecture they provide.Most interpreters are stack based, such as the Java Virtual machine,.net virtual machine, and the early V8 virtual machine. Stack-based virtual machines are simple and crisp when handling function calls, solving recursion problems, and switching contexts. whileToday’s V8 virtual machines have a register-based design, which saves some intermediate data into registers.

    Register-based interpreter architecture:Reference:How does the interpreter interpret the execution bytecode?

  • TurboFan: Compiler, which uses the type information collected by Ignitio to convert Bytecode into optimized assembly code;

  • Orinoco: Garbage Collector, a garbage collection module responsible for reclaiming memory space that is no longer needed by the program.

Parser, Ignition and TurboFan can compile JS source code into assembly code. The flow chart is as follows:In short,Parser converts JS source Code to AST, then Ignition converts AST to Bytecode, and finally TurboFan converts Bytecode to optimized Machine Code(actually assembly Code)..

  • If the function is not called, V8 does not compile it.
  • If the function is called only once, Ignition compiles it and Bytecode interprets it. TurboFan doesn’t do optimizations because it requires Ignition to collect the type information when the function is executed. This requires that the function be executed at least once before TurboFan can optimize the compilation.
  • TurboFan will eventually compile Bytecode into Optimized Machine Code if the function is called multiple times and the type information collected in Ignition proves that it can be Optimized. To improve the performance of code execution.

The red dotted line in the image is inverted, that is, Optimized Machine Code will be restored to Bytecode. This process is called Deoptimization. This is because Ignition may be collecting the wrong information, such as the add function whose arguments were integers and then became strings. The generated Optimized Machine Code already assumes that the parameters of the add function are integers, which is of course incorrect, so you need to deoptimize.

function add(x, y) {
  return x + y;
}

add(3.5);
add('3'.'5');
Copy the code

Before running C, C++, Java and other programs, you need to compile, can not directly execute the source code; For JavaScript, however, you can execute the source code directly (for example, Node test.js) by compiling it at runtime. This is called just-in-time compilation, or JIT. Therefore, V8 is also a JIT compiler.

How does the V8 engine work? 【 author: KiwenLau】

How does V8 execute a piece of JavaScript code

  • Prior to V8, all JavaScript virtual machines had interpreted execution, which was a major cause of slow JavaScript execution. V8 pioneered the introduction of just-in-time (JIT), a two-wheel drive design (mixing compiler and interpreter techniques), a tradeoff strategy that mixes compilation execution with interpreted execution, which greatly improves JavaScript execution speed. After the advent of V8, the major vendors also introduced JIT mechanism in their JavaScript virtual machines, so the current market JavaScript virtual machines have a similar architecture. In addition, V8 also introduced lazy compilation, inline caching, hidden classes and other mechanisms earlier than other virtual machines, further optimizing the compilation and execution efficiency of JavaScript code.

  • V8 executes a JavaScript flow chart:

    How does V8 execute a piece of JavaScript code?

  • V8 is essentially a virtual machine, and since computers can only recognize binary instructions, there are usually two ways to get a computer to execute a high-level language:

    • The first is to convert high-level code into binary code and have the computer execute it.
    • Another way is to install an interpreter on the computer and let the interpreter interpret and execute.
  • Interpreted execution and compiled execution have their own advantages and disadvantages. Interpreted execution is fast to start but slow to execute, while compiled execution is slow to start but fast to execute. Explained in order to make full use of advantages of executed and compilation, avoid its shortcomings, V8 adopted a trade-off strategy, adopting the tactics of interpretation in the process of start, but if one piece of code execution frequency exceeds a value, the V8 will optimize the compiler to compile it into execution efficiency more efficient machine code.

  • Conclusion:

    The main processes V8 goes through to execute a piece of JavaScript code include:

    • Initialize the base environment;
    • Parsing source code to generate AST and scope;
    • Generate bytecode based on AST and scope;
    • Interpret and execute bytecode;
    • Listen for hotspot code;
    • Optimize hot spot code for binary machine code;
    • Deoptimize the generated binary machine code.

First-class citizens with closures

The definition of first-class citizens

  • In a programming language, first-class citizens can be used as function arguments, as function return values, or assigned to variables.
  • If a function in a programming language can do the same thing as a data type in that language, we call the function in that language a first-class citizen. For example, strings are first-class citizens in almost all programming languages, strings can be used as function arguments, strings can be used as function return values, and strings can be assigned to variables. Functions are not necessarily first-class citizens for various programming languages, such as pre-8 versions of Java.
  • In JavaScript, functions can be assigned to variables, as function arguments, and as function return values, so functions are first-class citizens in JavaScript.

Dynamic and static scopes

  • If the scope of a language is statically scoped, the referential relationships between symbols can be determined by the program code at compile time and not at run time. Where a function is declared, it has the scope of its location. What variables can it access, so it’s bound to those variables, and it’s always going to be able to access those variables at run time. That is, the static scope can be determined by the program code and is fully determined at compile time. Most languages are statically scoped.
  • Dynamic Scope. That is, variable references and variable declarations are not bound at compile time. At run time, it is to dynamically find a variable with the same name in the run environment. Bash, a scripting language used on macOS or Linux, is dynamically scoped.

Three basic features of closures

  • The JavaScript language allows you to define new functions within a function
  • Variables defined in a parent function can be accessed in an internal function
  • Because functions in JavaScript are first-class citizens, a function can be the return value of another function
// Closure (static scope, first-class citizen, paradox of the call stack)
function foo() {
  var d = 20;
  return function inner(a, b) {
    const c = a + b + d;
    return c;
  };
}
const f = foo();
Copy the code

You can refer to my previous article about closures, but I won’t repeat it here. I’ll focus on the problems closures bring to Chrome V8 and how to solve them.

Inert parsing

Lazy parsing is when a parser encounters a function declaration during parsing, it skips the code inside the function and does not generate the AST and bytecode for it, but only the AST and bytecode for the top-level code.

  • V8 does not parse all the JavaScript into intermediate code at once during the compilation process, mainly because of the following two factors:
    • First, if all the JavaScript code is parsed and compiled at once, too much code will increase the compilation time, which will significantly slow down the first time the JavaScript code is executed and make the user feel stuck. Because sometimes a page’s JavaScript code is very large, if you have to parse and compile all the code at once, it will greatly increase the user’s waiting time;
    • Second, both the parsed bytecode and the compiled machine code are stored in memory, and if all the JavaScript code is parsed and compiled at once, the intermediate code and machine code will always occupy memory.
  • For these reasons, all major JavaScript virtual machines implement lazy parsing.
  • Closures present a problem for lazy parsing: d above cannot be destroyed with the execution context of foo.

Advance the parser

V8 introduced preparsers. For example, when a function is encountered while parsing top-level code, the preparser does a quick preparse of the function instead of simply skipping it.

  • If the current function has a syntax error, it will throw a syntax error to V8.
  • The function checks to see if an external variable is referenced inside the function. If so, the preresolver copies the stack variable into the heap and uses the reference directly from the heap the next time the function is executed, thus solving the closure problem.

How does V8 store objects internally: fast and slow properties

What does the following code output:

// test.js
function Foo() {
  this[200] = 'test-200';
  this[1] = 'test-1';
  this[100] = 'test-100';
  this['B'] = 'bar-B';
  this[50] = 'test-50';
  this[9] = 'test-9';
  this[8] = 'test-8';
  this[3] = 'test-3';
  this[5] = 'test-5';
  this['D'] = 'bar-D';
  this['C'] = 'bar-C';
}
var bar = new Foo();

for (key in bar) {
  console.log(`index:${key}  value:${bar[key]}`);
}
/ / output:
// index:1 value:test-1
// index:3 value:test-3
// index:5 value:test-5
// index:8 value:test-8
// index:9 value:test-9
// index:50 value:test-50
// index:100 value:test-100
// index:200 value:test-200
// index:B value:bar-B
// index:D value:bar-D
// index:C value:bar-C
Copy the code

The ECMAScript specification defines that numeric properties should be arranged in ascending order of index size, and string properties in ascending order of the order in which they were created. Here we refer to the number property in the object as the sort property, which in V8 is called elements, and the string property as the general property, which in V8 is called Properties. Within V8, two linear data structures are used to store the sorted and general properties, respectively, in order to effectively improve the performance of storing and accessing these two properties. V8 also stores some of the general properties directly into the object itself, which we call in-object properties, but the number of in-object properties is fixed, with the default being 10.

function Foo(property_num, element_num) {
  // Add indexable attributes
  for (let i = 0; i < element_num; i++) {
    this[i] = `element${i}`;
  }
  // Add general properties
  for (let i = 0; i < property_num; i++) {
    let ppt = `property${i}`;
    this[ppt] = ppt; }}var bar = new Foo(10.10);
Copy the code

You can take a snapshot of the current Memory view using the Memory TAB in the Chrome Developer tool. View the storage changes by increasing the first parameter.

We will be saved in the properties of linear data structure called “properties”, because only need by index in linear data structure that can access to the property, although the access speed of linear structure, but if you add or delete from the linear structure when a large number of attributes, the execution efficiency is very low, this is mainly because will produce a lot of time and memory overhead. Therefore, if an object has too many properties, V8 will adopt an alternative storage strategy, the “slow properties” strategy, but the slow property object will have a separate nonlinear data structure (dictionary) as the storage container for the properties. All attribute meta-information is no longer stored linearly, but is stored directly in the attribute dictionary.

V8 attribute storage:

Conclusion:

Since objects in JavaScript are made up of sets of properties and values, the easiest way to do this is to use a dictionary to hold the properties and values, but because dictionaries are non-linear, reading efficiency is greatly reduced. To improve search efficiency, V8 adds two hidden attributes to the object, the sort attribute and the general attribute. The element attribute points to the Elements object, where the sort attributes are stored in order. The properties property points to the Properties object, where the general properties are stored in the order in which they were created.

By introducing these two attributes, V8 speeds up the search for attributes. To further improve the search efficiency, V8 also implements a strategy of built-in attributes. When there are fewer than a certain number of general attributes, V8 writes them directly into the object, thus saving another intermediate step.

However, if there are too many properties in the object, or if there are repeated properties added or removed, V8 degrades the linear storage mode to the non-linear dictionary storage mode, which slows lookups but speeds up the modification of the object’s properties.

Fast and Slow properties: how does V8 speed up object property access?

Heap space and stack space

Stack space

  • Modern languages are based on the function of each function in the process of execution, has its own life cycle and scope, when at the end of the function, the scope will be destroyed, therefore, we will use the stack this data structure to manage the function invocation process, we also put the management in the process of the function call stack structure called the call stack.
  • The stack space is mainly used to manage JavaScript function calls. The stack is a continuous piece of memory, and the stack structure is “first in, last out” strategy. During a function call, everything that is context-sensitive is stored on the stack, such as the native type, the address of the referenced object, the execution state of the function, the this value, and so on. When a function finishes executing, the context in which the function was executed is destroyed.
  • Stack space is the biggest characteristic of space is continuous, so the address of each element in the stack are fixed, so the stack space search efficiency is very high, but usually in memory, it is difficult to assign to a large contiguous space, therefore, V8 do to the size of the stack space limit, if a function call level too deep, the V8 will likely throw stack overflow error.
  • Stack advantages and disadvantages:
    • The stack structure is well suited to the function call process.

    • Allocating and destroying resources on the stack is very fast, mainly because the stack space is contiguously allocated and destroying space only needs to move the pointer down.

    • Although the operation speed is very fast, but the stack also has disadvantages, the biggest disadvantage is also caused by its advantage, that is, the stack is continuous, so to allocate a contiguous large space in memory is very difficult, so the stack space is limited.

      / / stack overflow
      function factorial(n) {
        if (n === 1) {
          return 1;
        }
        return n * factorial(n - 1);
      }
      console.log(factorial(50000));
      Copy the code

Heap space

  • Heap space is a tree-like storage structure for storing discrete data of object types. In JavaScript, except for data of native types, all other object types, such as functions, arrays, and in browsers, window objects and document objects, are stored in heap space.
  • When the host starts V8, it creates both heap space and stack space, and the resulting new data is stored in both Spaces.

inheritance

Inheritance is the ability of one object to access properties and methods in another object. In JavaScript, we implement inheritance through prototypes and chains of prototypes.

Every JavaScript object contains a hidden property called __proto__, which we call the object’s prototype. __proto__ refers to another object in memory. We call the object __proto__ refers to the object’s prototype object, and that object has direct access to the methods or properties of its prototype object.

Inheritance in JavaScript is very simple. Every object has a stereotype property that points to the stereotype object, and when you look for that property, the JavaScript virtual machine looks up the stereotype layer by layer until it finds the right attribute.

Hidden attribute__proto__

var animal = {
  type: 'Default'.color: 'Default'.getInfo: function () {
    return `Type is: The ${this.type}The color isThe ${this.color}. `; }};var dog = {
  type: 'Dog'.color: 'Black'};Copy the code

Implement inheritance with __proto__ :

dog.__proto__ = animal;
dog.getInfo();
Copy the code

Usually hidden properties cannot be directly interoperated with using JavaScript. Although modern browsers make it possible for JavaScript to access the hidden __proto__ attribute, in real projects we should not access or modify the attribute directly through __proto__ for two main reasons:

  • First of all, this is a hidden property, not a standard definition;
  • Second,Using this property can cause serious performance problems. Because JavaScript optimizes many of the original object structures with hidden classes, you can modify them directly__proto__Will directly break the existing optimized structure, triggering V8 to refactor the hidden class of this object!

How does a constructor create an object?

In JavaScript, this combination of new plus constructors is used to create an object and implement its inheritance. However, the semantics implied in this way are too subtle. In order to attract Java programmers and syntactically exploit Java hot spots, JavaScript has been forced to include the incongruous keyword new.

function DogFactory(type, color) {
  this.type = type;
  this.color = color;
}
var dog = new DogFactory('Dog'.'Black');
Copy the code

When V8 executes the above code, it does a few things behind its back:

var dog = {};
dog.__proto__ = DogFactory.prototype;
DogFactory.call(dog, 'Dog'.'Black');
Copy the code

Machine code, bytecode

Why does V8 introduce bytecode

  • Early V8s, in order to speed up code execution, directly compiled JavaScript source code into unoptimized binary machine code. If a piece of binary code was executed too often, V8 would mark it as hot code. The hot code would be optimized by the optimized compiler, and the optimized machine code would execute more efficiently.

  • With the spread of mobile devices, the V8 team came to realize that there were two fatal problems with compiling JavaScript source code directly into binary code:

    • Time problem: the compilation time is too long, affecting the code startup speed;
    • Space issues: Caching compiled binaries takes up more memory.
  • These two problems undoubtedly hindered THE adoption of V8 on mobile devices, so the V8 team refactored the code extensively to introduce intermediate bytecode. Bytecode has three advantages:

    • Fix startup issues: bytecode generation time is short;
    • Solve the space problem: While bytecode takes up more space than raw JavaScript, it is still much smaller than machine code, and caching bytecode greatly reduces memory usage.
    • Clear code architecture: Using bytecode simplifies the complexity of the application, making it easier to port V8 to different CPU architecture platforms.
  • Bytecode is sort of assembly language, except it doesn’t have a specific CPU, or it has a virtual CPU. This makes it much easier to generate bytecodes without having to produce different code for different cpus. V8 supports nine different cpus. Introducing a mid-tier Bytecode simplifies V8 compilation and improves scalability.

  • If we generate bytecodes on different hardware, we will find that the instructions for generating the code are the same.

How do I view bytecode

// test.js
function add(x, y) {
  var z = x + y;
  return z;
}
console.log(add(1.2));
Copy the code

/d8./test.js –print-bytecode:

[generated bytecode for function: add (0x01000824fe59 <SharedFunctionInfo add>)]
Parameter count 3 # three arguments, including x and y passed explicitly, and this passed implicitly
Register count 1
Frame size 8
         0x10008250026 @    0 : 25 02             Ldar a1 # LoaD Accumulator from Register
         0x10008250028 @    2 : 34 03 00          Add a0, [0]
         0x1000825002b @    5 : 26 fb             Star r0 #Store Accumulator to Register #Store Accumulator to Register
         0x1000825002d @    7 : aa                Return  Ends execution of the current function and passes control back to the caller
Constant pool (size = 0)
Handler Table (size = 0)
Source Position Table (size = 0)
3
Copy the code

Common bytecode instructions:

  • Ldar: LoaD the value from the Register into the Accumulator. You can read it as LoaD the value from the Register.
  • Star: Store Accumulator Register. It is an Accumulator to Register
  • Add:Add a0, [0]Load the value from the A0 register and add it to the value in the accumulator, then put the result into the accumulator again.

    Add a0 [0] is called the Feedback vector slot, which is an array where the interpreter stores analysis information for the data types interpreted during execution. It is used to provide optimization information to the TurboFan optimization compiler. Much bytecode provides runtime information for the feedback vector slot.

  • LdaSmi: Loads small integers (Smi) into the accumulator register
  • Return: Terminates execution of the current function and passes control back to the caller. The value returned is the value in the accumulator.

The bytecode instruction set in V8

Hide classes and inline caching

JavaScript is a dynamic language, and its execution efficiency is lower than that of static languages. In order to improve the execution speed of JavaScript, V8 uses many features of static languages for reference, such as the IMPLEMENTATION of JIT mechanism and the introduction of hidden classes to improve the speed of object property access. Inline caching was introduced to speed up operations.

Why are static languages more efficient?

In static languages, such as C++, the structure of an object needs to be defined before it is declared, and the code needs to be compiled before execution. During compilation, the shape of each object is fixed, that is, it cannot be changed during the execution of the code. The value of an object’s property can be queried directly through an offset query, which is one reason why static languages are so efficient.

While JavaScript is running, object properties can be modified, so when V8 uses an object, such as obj. X, it does not know whether the object has an X in it, nor does it know how much x is offset from the object, which means V8 does not know the exact shape of the object. So, when you want to query the x attribute in object obj in JavaScript, V8 will do it step by step according to specific rules, which is very slow and time-consuming.

Introduce static features into V8

  • One of the ideas V8 uses is to statically object in JavaScript, which means that while V8 is running JavaScript, it assumes that objects in JavaScript are static.
  • Specifically, V8 makes two assumptions for each object:
    • No new properties are added once the object is created;
    • Properties are not deleted after the object is created.
  • With these two assumptions in place, V8 can make deep optimizations for objects in JavaScript. V8 creates one for each objectHidden classes, the hidden class of the object records some basic layout information of the object, including the following two points:
    • All the properties contained in an object;
    • The offset of each property relative to the object.
  • With the hidden class, when V8 accesses a property in an object, it will first look for the offset of the property relative to its object in the hidden class. With the offset and the property type, V8 can directly fetch the corresponding property value from memory without going through a series of search procedures. This greatly improves the efficiency of V8’s object lookup.
  • In V8, a hidden class is called a map. Each object has a map property whose value points to the hidden class in memory.
  • A map describes the memory layout of an object, such as what properties the object contains, and what the data corresponds to the offset of the object.

View hidden classes through D8

// test.js
let point1 = { x: 100.y: 200 };
let point2 = { x: 200.y: 300 };
let point3 = { x: 100 };
%DebugPrint(point1);
%DebugPrint(point2);
%DebugPrint(point3);
Copy the code
 ./d8 --allow-natives-syntax ./test.js
Copy the code
# = = = = = = = = = = = = = = =
DebugPrint: 0x1ea3080c5bc5: [JS_OBJECT_TYPE]
# V8 creates a hidden class for point1 objects
 - map: 0x1ea308284ce9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - elements: 0x1ea3080406e9 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties: 0x1ea3080406e9 <FixedArray[0]> {
    #x: 100 (const data field 0)
    #y: 200 (const data field 1)
 }
0x1ea308284ce9: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 20
 - inobject properties: 2
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - stable_map
 - back pointer: 0x1ea308284cc1 <Map(HOLEY_ELEMENTS)>
 - prototype_validity cell: 0x1ea3081c0451 <Cell value= 1>
 - instance descriptors (own) #2: 0x1ea3080c5bf5 <DescriptorArray[2]>
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - constructor: 0x1ea3082413b1 <JSFunction Object (sfi = 0x1ea3081c557d)>
 - dependent code: 0x1ea3080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
 - construction counter: 0

# = = = = = = = = = = = = = = =
DebugPrint: 0x1ea3080c5c1d: [JS_OBJECT_TYPE]
# V8 creates a hidden class for point2 objects
 - map: 0x1ea308284ce9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - elements: 0x1ea3080406e9 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties: 0x1ea3080406e9 <FixedArray[0]> {
    #x: 200 (const data field 0)
    #y: 300 (const data field 1)
 }
0x1ea308284ce9: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 20
 - inobject properties: 2
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - stable_map
 - back pointer: 0x1ea308284cc1 <Map(HOLEY_ELEMENTS)>
 - prototype_validity cell: 0x1ea3081c0451 <Cell value= 1>
 - instance descriptors (own) #2: 0x1ea3080c5bf5 <DescriptorArray[2]>
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - constructor: 0x1ea3082413b1 <JSFunction Object (sfi = 0x1ea3081c557d)>
 - dependent code: 0x1ea3080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
 - construction counter: 0

# = = = = = = = = = = = = = = =
DebugPrint: 0x1ea3080c5c31: [JS_OBJECT_TYPE]
# V8 creates a hidden class for point3 objects
 - map: 0x1ea308284d39 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - elements: 0x1ea3080406e9 <FixedArray[0]> [HOLEY_ELEMENTS]
 - properties: 0x1ea3080406e9 <FixedArray[0]> {
    #x: 100 (const data field 0)
 }
0x1ea308284d39: [Map]
 - type: JS_OBJECT_TYPE
 - instance size: 16
 - inobject properties: 1
 - elements kind: HOLEY_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - stable_map
 - back pointer: 0x1ea308284d11 <Map(HOLEY_ELEMENTS)>
 - prototype_validity cell: 0x1ea3081c0451 <Cell value= 1>
 - instance descriptors (own) #1: 0x1ea3080c5c41 <DescriptorArray[1]>
 - prototype: 0x1ea308241395 <Object map = 0x1ea3082801c1>
 - constructor: 0x1ea3082413b1 <JSFunction Object (sfi = 0x1ea3081c557d)>
 - dependent code: 0x1ea3080401ed <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
 - construction counter: 0
Copy the code

Multiple objects share a hidden class

  • In V8,Each object has a Map attribute, which points to the hidden class of the object. However,If two objects have the same shape, V8 will reuse the same hidden class for them, this has two advantages:
    • It reduces the creation times of hidden classes and indirectly accelerates the code execution speed.
    • Reduced storage space for hidden classes.
  • So, when do two objects have the same shape, if:
    • Same attribute name;
    • Equal number of properties.

Rebuild the hidden classes

  • Adding a new property to an object, deleting a new property, or changing the data type of a property changes the shape of the object, which triggers V8 to create a new hidden class for the shape-changed object.
// test.js
let point = {};
%DebugPrint(point);
point.x = 100;
%DebugPrint(point);
point.y = 200;
%DebugPrint(point);
Copy the code
# ./d8 --allow-natives-syntax ./test.js
DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
 - map: 0x32c7082802d9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 ...

DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
 - map: 0x32c708284cc1 <Map(HOLEY_ELEMENTS)> [FastProperties]
 ...

DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
 - map: 0x32c708284ce9 <Map(HOLEY_ELEMENTS)> [FastProperties]
 ...
Copy the code
  • Each time a new property is added to an object, the address of the object’s hidden class changes, which means that the hidden class changes as well. V8 also recreates the hidden class of an object if a property is removed and the shape of the object changes as a result;
  • Best practices
    • When initializing objects with literals, ensure that the order of properties is consistent;
    • Use literals whenever possible to initialize entire object properties at once;
    • Try to avoid the delete method.

Inline caching is used to improve function execution efficiency

While hidden classes can speed up the search for an object, V8’s search for an object’s property value involves finding the object’s hidden class and finding the object’s property value based on the hidden class. If an object property is used in a function, and the function is executed multiple times:

function loadX(obj) {
  return obj.x;
}
var obj = { x: 1.y: 3 };
var obj1 = { x: 3.y: 6 };
var obj2 = { x: 3.y: 6.z: 8 };
for (var i = 0; i < 90000; i++) {
  loadX(obj);
  loadX(obj1);
  // Produce polymorphisms
  loadX(obj2);
}
Copy the code

The usual process for V8 to get OBj.x is:

  • Find object obj’s hidden class;
  • Then find the x attribute offset through the hidden class;
  • In this code, the loadX function is executed repeatedly, so the process of obtaining obj. X also needs to be executed repeatedly.

Inline cache and its principles:

  • The loadX function is repeated many times in a for loop, so V8 does everything it can to compress the lookup process and improve the efficiency of the object lookup. The strategy for this acceleration is Inline Cache, or IC for short.
  • The principle of IC: In the process of executing a function, V8 looks at the key intermediate data at some call sites in the function, and then caches the data. The next time the function is executed, V8 can directly use the intermediate data, saving the process of obtaining the data again. Therefore, V8 uses IC, Can effectively improve the execution efficiency of some repetitive code.
  • The IC maintains a FeedBack Vector for each function. The FeedBack Vector records some key intermediate data during the execution of the function.
  • The feedback vector is actually a table structure consisting of a number of items, each of which is called a Slot, into which V8 will in turn write the intermediate data of the loadX function.
  • When V8 calls loadX again, such as when it executes the return obj.x statement in loadX, it will look for the offset of the x property in the corresponding slot, and then V8 can retrieve the obj.x property directly from memory. This greatly improves the performance of V8.

Singlets, polymorphisms, and superstates:

  • If a slot contains only 1 hidden class, then we call this state monomorphic;
  • If a slot contains 2 to 4 hidden classes, it is called a polymorphic state.
  • If there are more than 4 hidden classes in a slot, this state is called a magamorphic.
  • Singlets perform better than polymorphisms and superstates, so we need to avoid polymorphisms and superstates a little bit. To avoid polymorphisms and superstates, try to default all object properties to immutable. For example, if you write a loadX(obj) function, try not to use multiple obj objects with different shapes when passing arguments.

Conclusion: V8 introduced inline caching (IC), which listens for the execution of each function, burying listening points in key places such as Load, Store, and Call. V8 writes the data it listens to into a structure called a FeedBack Vector, and V8 maintains a FeedBack Vector for each function it executes. With the temporary data cached by the feedback vector, V8 can shorten the path through which object attributes are found, thus improving performance. However, for the same piece of code in a function, if the hidden classes of the object are different, then the feedback vector will also record these different hidden classes, which is a case of polymorphism and superstate. We try to avoid polymorphic or superstate situations in real projects.

Asynchronous programming and message queues

How does V8 execute callbacks

There are two types of callbacks: synchronous callbacks and asynchronous callbacks. Synchronous callbacks are executed inside the executing function, and asynchronous callbacks are executed outside the executing function.

General UI thread macro architecture:The UI thread provides oneThe message queue, and the events to be executed are added to the message queue, and then the UI thread iteratively takes events from the message queue and executes them. There are also two different types of asynchronous callbacks, typically setTimeout and XMLHttpRequest:

  • The execution process of setTimeout is actually relatively simple. The callback message is encapsulated inside the setTimeout function, and the callback message is added to the message queue. Then the main thread takes the callback event from the message queue and executes the callback function.
  • XMLHttpRequest is a bit more complicated because the download process needs to be executed in a separate thread, so when xmlHttprequest. send is executed, the host forwards the actual request to the network thread, then send exits, and the main thread continues to perform the following tasks. During the download, the network thread wraps some intermediate information and callback functions into a new message and adds it to the message queue, and then the main thread takes the callback event from the message queue and executes the callback function.

Macro tasks and microtasks

  • Call stack: A call stack is a data structure that manages the call relationships of functions executed on the main thread. In the process of executing the main thread, if the function call hierarchy is too deep, may cause the stack overflow error, we can use setTimeout to solve the stack overflow problem. The essence of setTimeout is to change a synchronous function call to an asynchronous one, where the callback function is encapsulated as a macro task and added to the message queue, and then the main thread circulates to read the next macro task from the message queue according to certain rules.

  • Macro tasks: Events in the message queue waiting to be executed by the main thread. As each macro task executes, V8 recreates the stack, then changes the stack as a function is called in the macro task, and finally, when the macro task finishes executing, the stack is cleared and the main thread continues to execute the next macro task.

  • Microtask: You can think of a microtask as a function that needs to be executed asynchronously, after the main function finishes and before the current macro task finishes.

  • The reason why microtasks are introduced in JavaScript is mainly because the granularity of the macro tasks in the message queue executed by the main thread is too coarse to be competent for some scenarios that require high precision and real-time performance. Microtasks can make an effective tradeoff between real-time performance and efficiency. In addition, using microtasks can change our current asynchronous programming model so that we can write asynchronous calls using synchronous form of code.

  • Microtasks are based on message queues, event loops, UI mainthreads, and stacks. From microtasks, coroutines, promises, generators, await/async, and other modern frontend techniques can be extended.

    // Does not cause the browser to jam
    function foo() {
      setTimeout(foo, 0);
    }
    foo();
    Copy the code

    Micro tasks:

// The browser console can cause the browser to get stuck (unable to respond to mouse events, etc.)
function foo() {
  return Promise.resolve().then(foo);
}
foo();
Copy the code
  • If a microtask is created in the current task, either promise.resolve () or promise.reject () will trigger the microtask. The triggered microtask will not be executed in the current function, so * executing a microtask will not cause the stack to expand indefinitely;
  • Unlike asynchronous calls, microtasks are still executed before the current task finishes, which means that no other tasks in the message queue can be executed until the current microtask finishes. Therefore, a microtask triggered inside a function must take precedence over a macro task triggered inside a function.
  • The microtask is still executed in the current task, so if new microtasks are triggered in a loop within the microtask, other tasks in the message queue will not have a chance to be executed.

History of front-end asynchronous programming schemes

  • The asynchronous programming model of Callback mode requires the implementation of a large number of Callback functions, which can disrupt the normal logic of the code, making it unlinear and difficult to read. This is known as Callback hell.
  • Promise was a great way to solve the problem of callback hell, and we could write the code in a linear way, and the process was linear and very intuitive.
  • However, this approach is full of Promise then() methods, and if the process is complex, the code will be full of THEN, not obvious semantics, and the code will not represent the execution process well. We want to write asynchronous code in a linear manner, and the ability to pause and resume function execution is critical to achieving this ideal. The generator can pause and resume functions, and we can use the same logic in the generator that we used to synchronize the code to asynchronize the code (the core of this logic is coroutines).
  • But in addition to the generator, we need a trigger to drive the execution of the generator. The end result on the front end is async/await. Async is a function that can pause and resume execution. Use await inside an async function to pause async function execution. If the Promise state changes to resolve or Reject, the async function resumes execution. Therefore, using async/await can achieve the goal of writing asynchronous code in a synchronous manner. Like generator functions, a function declared with async is executed as a separate coroutine. We can pause the coroutine with await. Since await is waiting for a Promise object, we can resolve to restore the coroutine.

Coroutines are more lightweight than threads. You can think of a coroutine as a task running on a thread. Multiple coroutines can exist on a thread, but only one can be executed on the thread at the same time. For example, if A coroutine is currently executing and B coroutine is to be started, then A coroutine needs to give control of the main thread to B coroutine. This is reflected in the suspension of A coroutine execution and the resumption of B coroutine execution. Similarly, you can start A coroutine from A B coroutine. In general, if A coroutine starts B coroutine from A coroutine, we call A coroutine the parent of B coroutine.

Just as a process can have multiple threads, a thread can have multiple coroutines. The thread can execute only one of the coroutines at a time. Most importantly, coroutines are not managed by the operating system kernel, but are completely program-controlled (that is, executed in user mode). The benefit of this is that performance is greatly improved without the resource consumption of thread switching.

Data expansion: the meaning and usage of co function library

The garbage collection

Garbage data

Starting from the “GC Roots” object, all objects in the GC Root are traversed. If there are no objects traversed by the GC Roots, these objects are garbage data. V8 will have a special garbage collector to collect this garbage data.

Garbage collection algorithm

Garbage collection can be roughly divided into the following steps:

  • The first step is to mark the active and inactive objects in the space by GC Root. V8 currently uses reachability to determine whether an object in the heap is an active object. Specifically, this algorithm takes some GC Roots as a set of initial surviving objects, starting from the GC Roots object, and traverses all objects in the GC Root:

    • Objects that can be accessed by GC Root are considered reachable, so they must be kept in memory. We also call reachable objects active objects.
    • Objects that are not traversed by GC Roots are unreachable, and these unreachable objects may be reclaimed. Unreachable objects are called inactive objects.
    • inThere are many GC roots in a browser environment, usually including (but not limited to) the following:
      • Global window object (in each iframe);
      • The document DOM tree consists of all the native DOM nodes that can be reached by traversing the document;
      • Store variables on the stack.
  • The second step is to reclaim the memory occupied by inactive objects. In fact, after all the tags are completed, all the objects marked as recyclable in memory are cleaned uniformly.

  • The third step, do memory collation. In general, after frequent collection of objects, there will be a large number of discontinuous memory space, we call these discontinuous memory space memory fragmentation. When there is a large amount of memory fragmentation in memory, it is possible to run out of memory if large contiguous memory needs to be allocated, so the last step is to defragment the memory. This step is optional, however, because some garbage collectors (such as secondary garbage collectors) do not generate memory fragmentation.

The garbage collection

  • V8 basisIntergenerational hypothesis, and divide the heap memory intoThe new generation and the old generationThere are two regions: the Cenozoic generation stores the objects with short life time, and the old generation stores the objects with long life time. The intergenerational hypothesis has two characteristics:
    • The first is that most objects are “ephemeral.” That is, most objects live in memory for a very short time, such as variables declared inside a function or in a block-level scope. Variables defined in that scope are destroyed when a function or block of code is executed. Therefore, once memory is allocated for such objects, they quickly become inaccessible;
    • The second is immortal objects that live longer, such as the global Window, DOM, Web API, and so on.
  • In order to improve the efficiency of garbage collection, V8 sets up two garbage collectors, the primary garbage collector and the secondary garbage collector.
    • The main garbage collector is responsible for collecting the garbage data of the old generation, and the secondary garbage collector is responsible for collecting the garbage data of the new generation.
    • Scavenge algorithm is adopted in the auxiliary garbage collectorIs To divide Cenozoic space in half into two areas (also known as From and To space in some places), half of which is the object area and half is the free area. New data is allocated in the object area. When the object area is nearly full, the garbage collector performs garbage collection. After that, the living objects are copied from the object area to the free area, and the two regions are exchanged.
      • This role reversal also allows the new generation to reuse these two areas indefinitely.
      • Vice garbage collector every time perform cleanup operations, all need to survive the object from the object region is copied to the free region, copy operation need time cost, if the new district space set too big, so every time cleaning time is too long, so in order to execute efficiency, generally relatively small space can be set up in the newborn.
      • The secondary garbage collector also uses an object promotion strategy, which is to move objects that have survived two garbage collections into the old generation.
    • Main Garbage collector The collector is mainly responsible forIn the old generation, the garbage data collection operation will go through the marking, cleaning and sorting process.
      • The main garbage collector is mainly responsible for garbage collection in the old generation. In addition to those who are promoted in the new generation, some big ones will be directly assigned to the old generation.
      • The objects in the old generation have two characteristics: one is that the object occupies a large space; The other is that the object lives longer.

Stop-The-World

Since JavaScript runs on top of the main thread, once the garbage collection algorithm is executed, the executing JavaScript script needs to be suspended until the garbage collection is complete before resuming script execution. We call this stop-the-world.

  • V8’s original garbage collector had two features:
    • The first is that garbage collection is performed on the main thread,
    • The second feature is that the entire garbage collection process is performed one at a time.
  • Because of these two reasons, it is easy to cause the main thread to stall, so V8 uses a number of schemes to optimize execution efficiency.
    • The first is parallel collection, where the garbage collector uses multiple helper threads to perform the collection in parallel during a complete garbage collection.
    • The second option is incremental garbage collection, where the garbage collector breaks down the marking work into smaller chunks that are interspersed between different tasks on the main thread. With incremental garbage collection, it is not necessary for the garbage collector to perform the entire garbage collection process at a time, but only a small part of the entire garbage collection process at a time.
    • The third option is concurrent collection. While the collection thread is executing the JavaScript process, the helper thread can complete the garbage collection operation in the background.
    • The primary garbage collector uses all of the schemes (concurrent marking, incremental marking, auxiliary cleaning), and the secondary garbage collector uses some of the schemes.

Like the stars of night, for whom the Luli midnight

Breaking the JavaScript Speed Limit with V8

Daniel Clifford gave a great talk at Google I/O 2012 called “Breaking the JavaScript Speed Limit with V8.” In his presentation, he explains in depth 13 simple code optimizations that can make your JavaScript code faster when compiling/running in Chrome V8. In his speech, he explained how to optimize and why. Below is a brief list of 13 JavaScript performance tips:

  1. Initializes the members of all objects in the constructor (so that these instances do not change their hidden classes later);
  2. Always initialize object members in the same order;
  3. Try to use numbers that can be represented as 31-bit signed integers;
  4. Use sequential primary keys starting at 0 for arrays;
  5. Don’t pre-allocate large arrays (say, larger than 64K elements) to their maximum size. Just let the size evolve.
  6. Do not delete elements from arrays, especially numeric arrays;
  7. Do not load uninitialized or deleted elements;
  8. For fixed-size arrays, initialize them using “Array literals” (as literals when initializing arrays of small rated sizes);
  9. Small arrays (less than 64K) are preallocated with the correct size before use;
  10. Do not store non-numeric values (objects) in numeric arrays.
  11. Try to use monomorphic rather than polymorphic (if you initialize a small array through non-literals, do not trigger the conversion of types);
  12. Don’t usetry{} catch{}(If anytry/catchIf the code is fast, put the performance-sensitive code in a nested function.
  13. Avoid modifying hidden classes in methods after optimization.

Speech data reference: Performance Tips for JavaScript in V8 JavaScript | V8 Performance tip | translation 】 【 network video | YouTube

5 tips for optimizing code in V8 engines

  1. Order of object properties: Be sure to use the same order when instantiating your object properties so that the hidden class and subsequent optimized code are shared;
  2. Dynamic properties: Adding properties after object instantiation forces the hidden class to change and slows down the execution of code optimized for the old hidden class. Therefore, all properties are allocated in the object’s constructor;
  3. Methods: Executing the same method repeatedly will run faster than executing different methods only once (because of inline caching);
  4. Arrays: Avoid sparse arrays with non-incrementing keys. This is actually a hash table. Every element in this array is expensive to get. Also, avoid applying for large arrays in advance. The best thing to do is to grow the array slowly as you need it. Finally, do not delete elements from the array, as this makes keys sparse;
  5. Tagged values: V8 uses 32 bits to represent objects and numbers. It uses one bit to distinguish whether it is an object (flag = 1) or an integer (flag = 0) and is also called a small integer (SMI) because it has only 31 bits. Then, if a value is greater than 31 bits, V8 will box it, convert it to a double, and create a new object to hold the number. So, to avoid costly box operations, try to use 31 bits of signed numbers.

References: How JavaScript works: inside the V8 engine + 5 tips on How to write optimized code | translation

JavaScript types: What details about types you don’t know? | JavaScript packing and unpacking

JavaScript startup performance bottleneck analysis and solution

References: JavaScript Start – up Performance | JavaScript startup Performance bottleneck analysis and solution

Sometimes as the universe, continuous no unique period

  • V8 Official Documentation
  • The illustration Google V8
  • How browsers work and practice
  • How JavaScript Works: An Overview of the Engine, runtime, and call Stack
  • How JavaScript Works: The Rise of Event Loops and Asynchronous Programming + 5 tips on how to write better with Async /await

The resources

  • The illustration Google V8
  • How does the V8 engine Work? 【 author: KiwenLau】

Set pieces

  • Console ImporterEasily import JS and CSS resources from Chrome Console.

    Effect:

This article was first published on my personal blog. This article is published simultaneously and maintained in the yusef community.