First article: github.com/qiudongwei/…

Test a piece of code

Operating environment: Chrome JavaScript | V8 8.9.255.20

let array1 = [1];
let array2 = [1];
// array2[2]=2; // Exceeds the array length

console.time('array1');  
for (let i = 0; i < 10000000; i++) {  
    array1.push(i); 
}  
console.timeEnd('array1'); 


console.time('array2');  
for (let j = 0; j < 10000000; j++) {  
    array2.push(j); 
}  
console.timeEnd('array2'); 
Copy the code

Array1 and Array2 take almost the same time. ② Uncomment the third line and run it again. The test shows that the time of array1 is obviously less than that of array2.

Or just test it here:jsben

Why is that? How is array manipulation handled internally in V8?

The element type in an array

At the JavaScript level, you can assign any base data type to the elements of an array, eg: arr = [1, 1.2, ‘hello’, {}, []]. At the V8 level, however, it understands only three types of elements:

  • SMI_ELEMENTS: Also known as Smi, Small integers
  • DOUBLE_ELEMENTS: double precision floating point numbers, floating point numbers and integers that cannot be represented as Smi
  • ELEMENTS: Regular ELEMENTS that cannot be expressed as Smi or double values

For example, ignore the prefix PACKED_ for a moment:

const arr = [1.2.3]; // Element type: PACKED_SMI_ELEMENTS
arr.push(1.54);         // The element type becomes PACKED_DOUBLE_ELEMENTS
arr.push('c');            // The element type becomes PACKED_ELEMENTS
Copy the code

By the example, the V8 type SMI_ELEMENTS allocation for each array element | DOUBLE_ELEMENTS | ELEMENTS, and only from a specific element type conversion (eg: SMI_ELEMENTS) to a more general (eg: ELEMENTS).

Here’s another example:

const arr = [1.2.4]; // HOLEY_SMI_ELEMENTS
arr.push(5);               // The element type changes to HOLEY_DOUBLE_ELEMENTS
arr.push('a');             // The element type changes to HOLEY_ELEMENTS
Copy the code

In this example, the arR has a hole (not fully filled) that we call a sparse arrayHOLEYTo identify its type; PACKEDThat means this is a dense array. A sparse array is irreversible, and once marked empty, it always has holes, even if it’s filled! A sparse array cannot be converted into a dense array, and vice versa. The following diagram illustrates the conversion relationship between the element types:

The sparsity of the array may affect the performance of the array operation (if the data volume is large enough).

Array performance issues

  1. For sparse arrays, V8 requires extra checking and expensive lookups of its prototype chain;
  2. In general, more specific types of elements can be fine-grained. The more general the element type, the slower the operation on that object will be;
  3. V3 relies on its inline cache mechanism to optimize array operations on specific types of arrays more effectively.

Avoid creating sparse arrays

  1. avoidnew ArrayCreate an array
// bad
const arr = new array(5);

// good
const arr = [0.0.0.0.0];

// better
const arr = Array.from({length: 5}
Copy the code
  1. Avoid out-of-bounds access to arrays
const arr = [1.2.3];

// bad
log(arr[3]);
Copy the code
  1. To avoidlengthThe assignment
const arr = [1.2.3];

// bad
arr.length = 5;
Copy the code

An array of polymorphic

If your code needs to deal with arrays of multiple different element types, it may be slower than arrays of a single element type, because your code is polymorphic on array elements of different types. Look at this example:

const each = (array, callback) = > {
  for (let index = 0; index < array.length; ++index) {
    constitem = array[index]; callback(item); }};// First call
each(['a'.'b'.'c'], doSomething);

// Second call
each([1.1.2.2.3.3], doSomething);

// Third call
each([1.2.3], doSomething);
Copy the code

On the first call, the array type is PACKED_ELEMENTS, V8 uses inline cache. Remember that each is called with a specific element type. On the next call, V8 checks whether the array type is PACKED_ELEMENTS. The code generated last time will be reused, otherwise more will be done, such as regenerating the corresponding code;

On the second call to the array type PACKED_DOUBLE_ELEMENTS, V8 detects that the array type is different from the one in the cache and marks each as polymorphic. Each subsequent call to each requires PACKED_ELEMENTS and PACKED_DOUBLE_ELEMENTS checks, which incur some performance cost;

At the end of the third call, there are three different types of each caches in V8’s cache, and V8 does a type check for each subsequent each call in order to reuse the generated code.

However, a built-in approach (eg: array.prototype.foreach) can handle this polymorphism more efficiently, so it is preferred in performance-sensitive applications.

Reference:

Element types and performance optimization in V8