Types d'élément dans V8

Une chaîne arbitraire peut être utilisée comme nom de propriété pour un objet JavaScript. Mais pour certains sous-ensembles spéciaux de noms, il est logique de faire des optimisations spéciales dans les moteurs JavaScript. Un tel cas est celui des indices de tableau numérique .



Bien que dans la plupart des cas, ces propriétés ne se distinguent pas des autres, le moteur V8, à des fins d'optimisation, les stocke séparément des autres et les traite de manière spéciale. À l'intérieur de V8, ces propriétés sont appelées éléments ( éléments ) de l'objet. Assez logique: les objets ont des propriétés accessibles par nom, et les tableaux ont des éléments accessibles par index.



Grades élémentaires de base



Pendant l'exécution du code JavaScript, V8 garde une trace du type d'éléments de chaque tableau - quels éléments il stocke. Ces informations permettent au moteur de mieux optimiser ses opérations de baie. Par exemple, des fonctions intégrées comme map, reduceou sont forEachspécialisés pour chaque type d'élément.



Considérez, par exemple, un tableau comme celui-ci:



const array = [1, 2, 3];


Quels éléments contient-il? Du point de vue de l'opérateur, typeoftout est simple - ce sont des éléments du type number. Et c'est tout ce que l'on peut dire à leur sujet depuis JavaScript: le langage ne distingue pas int, floatet double. Cependant, il existe des différences au niveau du moteur. Le type d'éléments de ce tableau est PACKED_SMI _ELEMENTS. En termes V8, SMI est un format spécial pour stocker de petits entiers. Ce qui signifie que PACKEDnous y reviendrons un peu plus tard.



L'ajout d'un nombre fractionnaire à un tableau rend ses éléments plus généraux:



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


L'ajout d'une ligne rend le type d'éléments encore plus général:



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


Il existe trois principaux types d'éléments dans ce code:



  • SMI_ELEMENTS - pour les petits entiers
  • DOUBLE_ELEMENTS - pour les nombres Ă  virgule flottante et les entiers trop grands pour 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 . :



Grille des grades d'éléments



. , 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