Jenis Elemen di V8

String arbitrer dapat digunakan sebagai nama properti untuk objek JavaScript. Tetapi untuk beberapa subset khusus dari nama, masuk akal untuk melakukan pengoptimalan khusus di mesin JavaScript. Salah satu kasus tersebut adalah indeks array numerik .



Meskipun dalam banyak kasus properti ini berperilaku tidak dapat dibedakan dari yang lain, mesin V8, untuk tujuan pengoptimalan, menyimpannya secara terpisah dari yang lain dan memprosesnya dengan cara khusus. Di dalam V8 properti seperti itu disebut elemen ( elemen ) dari objek. Cukup logis: objek memiliki properti yang dapat diakses dengan nama, dan array memiliki elemen yang dapat diakses oleh indeks.



Nilai elemen dasar



Selama eksekusi kode JavaScript, V8 melacak jenis elemen dari setiap array - elemen mana yang disimpannya. Informasi ini memungkinkan mesin untuk lebih mengoptimalkan operasi lariknya. Misalnya, fungsi bawaan seperti map, reduceatau forEachdikhususkan untuk setiap jenis elemen.



Pertimbangkan, misalnya, array seperti ini:



const array = [1, 2, 3];


Elemen apa yang dikandungnya? Dari sudut pandang operator, typeofsemuanya sederhana - mereka adalah elemen tipe number. Dan hanya ini yang dapat dikatakan tentang mereka dari dalam JavaScript: bahasanya tidak membedakan int, floatdan double. Namun, ada perbedaan di level mesin. Jenis elemen dari array ini adalah PACKED_SMI _ELEMENTS. Dalam istilah V8, SMI adalah format khusus untuk menyimpan bilangan bulat kecil. Artinya PACKEDkita akan melihat sedikit nanti.



Menambahkan bilangan pecahan ke larik membuat elemennya lebih umum:



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


Menambahkan garis ke dalamnya membuat jenis elemen menjadi lebih umum:



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


Ada tiga macam elemen utama dalam kode ini:



  • SMI_ELEMENTS - untuk bilangan bulat kecil
  • DOUBLE_ELEMENTS - untuk bilangan floating point dan bilangan bulat yang terlalu besar 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 . :



Kisi nilai elemen



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