Tipos de elemento em V8

Uma string arbitrária pode ser usada como um nome de propriedade para um objeto JavaScript. Mas, para alguns subconjuntos especiais de nomes, faz sentido fazer otimizações especiais em mecanismos JavaScript. Um desses casos são os índices de matriz numérica .



Embora na maioria dos casos essas propriedades se comportem indistinguivelmente de qualquer outra, o motor V8, para fins de otimização, as armazena separadamente das demais e as processa de maneira especial. No V8, essas propriedades são chamadas de elementos ( elementos ) do objeto. Muito lógico: os objetos têm propriedades acessíveis por nome e os arrays têm elementos acessíveis por índice.



Graus de elemento básico



Durante a execução do código JavaScript, o V8 controla os tipos de elementos de cada array - quais elementos ele armazena. Essas informações permitem que o mecanismo otimize melhor as operações do array. Por exemplo, funções integradas como map, reduceou são forEachespecializadas para cada tipo de elemento.



Considere, por exemplo, uma matriz como esta:



const array = [1, 2, 3];


Que elementos contém? Do ponto de vista do operador, typeoftudo é simples - são elementos do tipo number. E isso é tudo que pode ser dito sobre eles a partir de dentro JavaScript: a linguagem não distingue int, floate double. No entanto, existem diferenças no nível do motor. O tipo de elementos dessa matriz é PACKED_SMI _ELEMENTS. Em termos de V8, SMI é um formato especial para armazenar pequenos inteiros. O que significa PACKEDque veremos um pouco mais tarde.



Adicionar um número fracionário a uma matriz torna seus elementos mais gerais:



const array = [1, 2, 3];
//  : PACKED_SMI_ELEMENTS
array.push(4.56);
//  : PACKED_DOUBLE_ELEMENTS


Adicionar uma linha torna o tipo de elementos ainda mais geral:



const array = [1, 2, 3];
//  : PACKED_SMI_ELEMENTS
array.push(4.56);
//  : PACKED_DOUBLE_ELEMENTS
array.push('x');
//  : PACKED_ELEMENTS


Existem três tipos principais de elementos neste código:



  • SMI_ELEMENTS - para pequenos inteiros
  • DOUBLE_ELEMENTS - para números de ponto flutuante e inteiros muito grandes para SMI
  • ELEMENTS — , SMI DOUBLE


, . , PACKED_ELEMENTS PACKED_DOUBLE_ELEMENTS.



:



  • V8 .
  • — , .
  • .


PACKED HOLEY



(dense), (packed), . "" ( , ) (sparse), "" (holey):



const array = [1, 2, 3, 4.56, 'x'];
//  : PACKED_ELEMENTS
array.length; // 5
array[9] = 1; //  array[5]  array[9]  
//  : HOLEY_ELEMENTS


V8 , . , , .



(SMI_ELEMENTS, DOUBLE_ELEMENTS ELEMENTS) (PACKED), (HOLEY), . , PACKED_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS, HOLEY_SMI_ELEMENTS.



:



  • (PACKED), (HOLEY).
  • , .
  • PACKED- HOLEY- ( ).




V8 . :



Grade de graus de elemento



. , DOUBLE. , DOUBLE. , , - HOLEY, PACKED.



, V8 29 ( 22 — . .). .



, , . , . .





, . , V8 , .





, , . , , . , , array[42], array.length === 5. 42 , , . , V8 , , , .



, :



for (let i = 0, item; (item = items[i]) != null; i++) {
  doSomething(item);
}


items[items.length], .



:



for (let index = 0; index < items.length; index++) {
  const item = items[index];
  doSomething(item);
}


, items — iterable- ( , ), for-of:



for (const item of items) {
  doSomething(item);
}


forEach:



items.forEach((item) => {
  doSomething(item);
});


, for-of forEach for.



, ! :



function maximum(array) {
  let max = 0;
  for (let i = 0; i <= array.length; i++) { //   
    if (array[i] > max) max = array[i];
  }
  return max;
}


array[array.length], , : , , V8 undefined. , .





, , , V8 .



, . , -0 PACKED_DOUBLE_ELEMENTS.



const array = [3, 2, 1, +0];
// PACKED_SMI_ELEMENTS
array.push(-0);
// PACKED_DOUBLE_ELEMENTS


, , .



-0, -0 +0 (, , ).



NaN Infinity. , , SMI_ELEMENTS DOUBLE_ELEMENTS.



const array = [3, 2, 1];
// PACKED_SMI_ELEMENTS
array.push(NaN, Infinity);
// PACKED_DOUBLE_ELEMENTS


, . , PACKED_SMI_ELEMENTS, .



, , . .



array-like objects



JavaScript, — , DOM API — , . " " (array-like) :



const arrayLike = {};
arrayLike[0] = 'a';
arrayLike[1] = 'b';
arrayLike[2] = 'c';
arrayLike.length = 3;


length. . :



Array.prototype.forEach.call(arrayLike, (value, index) => {
  console.log(`${ index }: ${ value }`);
});
// : '0: a', '1: b', '2: c'.


forEach , . , , array-like - , :



const actualArray = Array.prototype.slice.call(arrayLike, 0);
actualArray.forEach((value, index) => {
  console.log(`${ index }: ${ value }`);
});
// : '0: a', '1: b', '2: c'.


.



, arguments — . , , :



const logArgs = function() {
  Array.prototype.forEach.call(arguments, (value, index) => {
    console.log(`${ index }: ${ value }`);
  });
};
logArgs('a', 'b', 'c');
// : '0: a', '1: b', '2: c'.


arguments rest parameters, , ECMAScript 2015. , .



function logArgs(...args) {
  args.forEach((value, index) => {
    console.log(`${ index }: ${ value }`);
  });
};
logArgs('a', 'b', 'c');
// : '0: a', '1: b', '2: c'.


arguments.



, array-like , .





, , , . :



const each = (array, callback) => {
  for (let index = 0; index < array.length; ++index) {
    const item = array[index];
    callback(item);
  }
};
const doSomething = (item) => console.log(item);

each([], () => {});

each(['a', 'b', 'c'], doSomething);
// `each`       `PACKED_ELEMENTS`.
//  V8   inline- (inline cache, IC),  `each`
//       . V8 
// ,   ,   `array.length`
//  `array[index]`  -      ,
//   ,      .   
//  `each`     .   
// `PACKED_ELEMENTS`,  V8   .  ,
//  .

each([1.1, 2.2, 3.3], doSomething);
// `each`       `PACKED_DOUBLE_ELEMENTS`.
// - ,   V8   `each`   ,
//  `array.length`  `array[index]`   .
//         
// ,      .

each([1, 2, 3], doSomething);
// `each`       `PACKED_SMI_ELEMENTS`.
//           `each`,
//     .


Array.prototype.forEach , , , .



, V8, .





. V8, . , :



const array = new Array(3);
//   ,    
// `HOLEY_SMI_ELEMENTS`,  ,   
//  

array[0] = 'a';
// ,    ,  !
//    `HOLEY_ELEMENTS`.

array[1] = 'b';
array[2] = 'c';
//      ,     
//     `HOLEY_ELEMENTS`   
// `PACKED_ELEMENTS`.


, , — !



:



const array = ['a', 'b', 'c'];
//  : PACKED_ELEMENTS


, , push.



const array = [];
// ...
array.push(someValue);
// ...
array.push(someOtherValue);




, , d8 ( jsvu). :



out/x64.debug/d8 --allow-natives-syntax


REPL d8, . %DebutPrint(object) ( elements):



d8> const array = [1, 2, 3]; %DebugPrint(array);
DebugPrint: 0x1fbbad30fd71: [JSArray]
 - map = 0x10a6f8a038b1 [FastProperties]
 - prototype = 0x1212bb687ec1
 - elements = 0x1fbbad30fd19 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
 - length = 3
 - properties = 0x219eb0702241 <FixedArray[0]> {
    #length: 0x219eb0764ac9 <AccessorInfo> (const accessor descriptor)
 }
 - elements= 0x1fbbad30fd19 <FixedArray[3]> {
           0: 1
           1: 2
           2: 3
 }
[...]


--trace-elements-transitions. , V8 .



$ cat my-script.js
const array = [1, 2, 3];
array[3] = 4.56;

$ out/x64.debug/d8 --trace-elements-transitions my-script.js
elements transition [PACKED_SMI_ELEMENTS -> PACKED_DOUBLE_ELEMENTS] in ~+34 at x.js:2 for 0x1df87228c911 <JSArray[3]> from 0x1df87228c889 <FixedArray[3]> to 0x1df87228c941 <FixedDoubleArray[22]>



All Articles