Slide 1

Slide 1 text

@mathias V8 internals for JavaScript developers @mathias // @v8js

Slide 2

Slide 2 text

@mathias

Slide 3

Slide 3 text

@mathias Elements kinds in V8

Slide 4

Slide 4 text

@mathias const array = [1, 2, 3];


Slide 5

Slide 5 text

@mathias const array = [1, 2, 3];
 // elements kind: PACKED_SMI_ELEMENTS


Slide 6

Slide 6 text

@mathias const array = [1, 2, 3];
 // elements kind: PACKED_SMI_ELEMENTS
 array.push(4.56);


Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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


Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

@mathias Smi Doubles Regular elements Elements kinds

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

@mathias array.length; // 5
 index 0 1 2 3 4 value 1 2 3 4.56 'x'

Slide 13

Slide 13 text

@mathias array.length; // 5
 array[9] = 1; // array[5] until array[8] are now holes
 index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 14

Slide 14 text

@mathias array.length; // 5
 array[9] = 1; // array[5] until array[8] are now holes
 // elements kind: HOLEY_ELEMENTS index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 15

Slide 15 text

@mathias array[8];
 // " ??? index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 16

Slide 16 text

@mathias array[8];
 // " ??? ❌ index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 17

Slide 17 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 18

Slide 18 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 19

Slide 19 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 20

Slide 20 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false ❌ index 0 1 2 3 4 5 6 7 8 9 value 1 2 3 4.56 'x' 1

Slide 21

Slide 21 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false ❌ hasOwnProperty(Array.prototype, '8');
 // " false

Slide 22

Slide 22 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false ❌ hasOwnProperty(Array.prototype, '8'); // " false ❌

Slide 23

Slide 23 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false ❌ hasOwnProperty(Array.prototype, '8'); // " false ❌ hasOwnProperty(Object.prototype, '8'); // " false

Slide 24

Slide 24 text

@mathias array[8];
 // " ??? ❌
 8 >= 0 && 8 < array.length; // bounds check // " true ❌ hasOwnProperty(array, '8');
 // " false ❌ hasOwnProperty(Array.prototype, '8'); // " false ❌ hasOwnProperty(Object.prototype, '8'); // " false ✅

Slide 25

Slide 25 text

@mathias array[8];
 // " undefined ✅
 8 >= 0 && 8 < array.length; // bounds check // " true hasOwnProperty(array, '8');
 // " false hasOwnProperty(Array.prototype, '8'); // " false hasOwnProperty(Object.prototype, '8'); // " false ✅

Slide 26

Slide 26 text

@mathias packedArray[8];
 // " undefined ✅
 8 >= 0 && 8 < packedArray.length; // bounds check // " true ✅ hasOwnProperty(packedArray, '8');
 // " true ✅ hasOwnProperty(Array.prototype, '8'); // " false ✅ hasOwnProperty(Object.prototype, '8'); // " false ✅

Slide 27

Slide 27 text

@mathias packedArray[8];
 // " undefined ✅
 8 >= 0 && 8 < packedArray.length; // bounds check // " true ✅ hasOwnProperty(packedArray, '8');
 // " true ✅ hasOwnProperty(Array.prototype, '8'); // " false ✅ hasOwnProperty(Object.prototype, '8'); // " false ✅

Slide 28

Slide 28 text

@mathias array[0];
 // " ???


Slide 29

Slide 29 text

@mathias array[0];
 // " ??? ❌

Slide 30

Slide 30 text

@mathias array[0];
 // " ??? ❌ 0 >= 0 && 0 < array.length; // bounds check // " true

Slide 31

Slide 31 text

@mathias array[0];
 // " ??? ❌ 0 >= 0 && 0 < array.length; // bounds check // " true ❌

Slide 32

Slide 32 text

@mathias array[0];
 // " ??? ❌ 0 >= 0 && 0 < array.length; // bounds check // " true ❌
 hasOwnProperty(array, '0');
 // " true

Slide 33

Slide 33 text

@mathias array[0];
 // " ??? ❌ 0 >= 0 && 0 < array.length; // bounds check // " true ❌
 hasOwnProperty(array, '0');
 // " true ✅

Slide 34

Slide 34 text

@mathias array[0];
 // " 1 ✅ 0 >= 0 && 0 < array.length; // bounds check // " true
 hasOwnProperty(array, '0');
 // " true ✅

Slide 35

Slide 35 text

@mathias PACKED > HOLEY

Slide 36

Slide 36 text

@mathias PACKED > HOLEY #$ %&

Slide 37

Slide 37 text

@mathias Smi Doubles Regular elements Elements kinds

Slide 38

Slide 38 text

@mathias Smi, packed Doubles, packed Regular elements, packed Smi, holey Doubles, holey Regular elements, holey

Slide 39

Slide 39 text

lattice

Slide 40

Slide 40 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS

Slide 41

Slide 41 text

@mathias Array.prototype.forEach PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Chrome 59

Slide 42

Slide 42 text

@mathias Array.prototype.forEach PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Chrome 61

Slide 43

Slide 43 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.forEach Chrome 64

Slide 44

Slide 44 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.map Chrome 64

Slide 45

Slide 45 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.filter Chrome 64

Slide 46

Slide 46 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.some Chrome 64

Slide 47

Slide 47 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.every Chrome 64

Slide 48

Slide 48 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.reduce Chrome 64

Slide 49

Slide 49 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array.prototype.reduceRight Chrome 64

Slide 50

Slide 50 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array#{find,findIndex} ' Chrome 64

Slide 51

Slide 51 text

@mathias

Slide 52

Slide 52 text

@mathias PACKED_SMI_ELEMENTS HOLEY_SMI_ELEMENTS PACKED_DOUBLE_ELEMENTS HOLEY_DOUBLE_ELEMENTS PACKED_ELEMENTS HOLEY_ELEMENTS Array#{find,findIndex} Chrome 70

Slide 53

Slide 53 text

@mathias const array = new Array(3);


Slide 54

Slide 54 text

@mathias const array = new Array(3);
 index 0 1 2 value

Slide 55

Slide 55 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 index 0 1 2 value

Slide 56

Slide 56 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 array[0] = 'a';
 index 0 1 2 value 'a'

Slide 57

Slide 57 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 array[0] = 'a';
 // HOLEY_ELEMENTS index 0 1 2 value 'a'

Slide 58

Slide 58 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 array[0] = 'a';
 // HOLEY_ELEMENTS array[1] = 'b';
 index 0 1 2 value 'a' 'b'

Slide 59

Slide 59 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 array[0] = 'a';
 // HOLEY_ELEMENTS array[1] = 'b';
 array[2] = 'c';
 index 0 1 2 value 'a' 'b' 'c' ( now packed! (

Slide 60

Slide 60 text

@mathias const array = new Array(3);
 // HOLEY_SMI_ELEMENTS
 array[0] = 'a';
 // HOLEY_ELEMENTS array[1] = 'b';
 array[2] = 'c';
 // HOLEY_ELEMENTS (still!) ( now packed! ( but it’s too late ) index 0 1 2 value 'a' 'b' 'c'

Slide 61

Slide 61 text

@mathias const array = ['a', 'b', 'c'];
 // elements kind: PACKED_ELEMENTS


Slide 62

Slide 62 text

@mathias const array = ['a', 'b', 'c'];
 // elements kind: PACKED_ELEMENTS
 
 // …
 array.push(someValue);
 array.push(someOtherValue);

Slide 63

Slide 63 text

@mathias Avoid holes! #ProTip Avoid holes

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

@mathias for (let i = 0, item; (item = items[i]) != null; i++) {
 doSomething(item);
 } 
 for (let index = 0; index < items.length; index++) {
 const item = items[index]; doSomething(item);
 }

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

@mathias Avoid holes! #ProTip Avoid out-of-bounds reads

Slide 70

Slide 70 text

@mathias +0 === -0; // " true

Slide 71

Slide 71 text

@mathias +0 === -0; // " true Object.is(+0, -0); // " false

Slide 72

Slide 72 text

@mathias [3, 2, 1, +0];
 // PACKED_SMI_ELEMENTS


Slide 73

Slide 73 text

@mathias [3, 2, 1, +0];
 // PACKED_SMI_ELEMENTS
 [3, 2, 1, -0];
 // PACKED_DOUBLE_ELEMENTS


Slide 74

Slide 74 text

@mathias [3, 2, 1, +0];
 // PACKED_SMI_ELEMENTS
 [3, 2, 1, -0];
 // PACKED_DOUBLE_ELEMENTS
 [3, 2, 1, NaN, Infinity];
 // PACKED_DOUBLE_ELEMENTS

Slide 75

Slide 75 text

@mathias Avoid holes! #ProTip Avoid elements kind transitions

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

@mathias const logArgs = (...args) => {
 args.forEach((value, index) => {
 console.log(`${ index }: ${ value }`);
 });
 };
 logArgs('a', 'b', 'c');
 // This logs '0: a', then '1: b', and finally '2: c'.

Slide 81

Slide 81 text

@mathias Avoid holes! #ProTip Prefer arrays over array-like objects

Slide 82

Slide 82 text

@mathias $


Slide 83

Slide 83 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8


Slide 84

Slide 84 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax


Slide 85

Slide 85 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8>

Slide 86

Slide 86 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8> const array = [1, 2,, 3];

Slide 87

Slide 87 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8> const array = [1, 2,, 3]; %DebugPrint(array);


Slide 88

Slide 88 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8> const array = [1, 2,, 3]; %DebugPrint(array);
 DebugPrint: 0x313389e0e551: [JSArray]
 - map = 0x3133e0582889 [FastProperties]
 - prototype = 0x313360387f81
 - elements = 0x313389e0e4c9 [HOLEY_SMI_ELEMENTS (COW)]
 - length = 4
 - properties = 0x3133dae02241 {
 #length: 0x31336c242839 (const accessor descriptor)
 }
 …

Slide 89

Slide 89 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8> const array = [1, 2,, 3]; %DebugPrint(array);
 DebugPrint: 0x313389e0e551: [JSArray]
 - map = 0x3133e0582889 [FastProperties]
 - prototype = 0x313360387f81
 - elements = 0x313389e0e4c9 [HOLEY_SMI_ELEMENTS (COW)]
 - length = 4
 - properties = 0x3133dae02241 {
 #length: 0x31336c242839 (const accessor descriptor)
 }
 …

Slide 90

Slide 90 text

@mathias $ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
 V8 version 7.0.244 (candidate)
 d8> const array = [1, 2,, 3]; %DebugPrint(array);
 DebugPrint: 0x313389e0e551: [JSArray]
 - map = 0x3133e0582889 [FastProperties]
 - prototype = 0x313360387f81
 - elements = 0x313389e0e4c9 [HOLEY_SMI_ELEMENTS (COW)]
 - length = 4
 - properties = 0x3133dae02241 {
 #length: 0x31336c242839 (const accessor descriptor)
 }
 … *

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

Avoid holes. — J.K. Rowling

Slide 93

Slide 93 text

Avoid holes. Avoid out-of-bounds reads. — ancient Chinese proverb

Slide 94

Slide 94 text

Avoid holes. Avoid out-of-bounds reads. Avoid elements kind transitions. — Justin Bieber

Slide 95

Slide 95 text

Avoid holes. Avoid out-of-bounds reads. Avoid elements kind transitions. Prefer arrays over array-like objects. — Albert Einstein

Slide 96

Slide 96 text

Avoid holes. Avoid out-of-bounds reads. Avoid elements kind transitions. Prefer arrays over array-like objects. Eat your vegetables. — this slide, just now

Slide 97

Slide 97 text

@mathias One more thing…

Slide 98

Slide 98 text

@mathias const array = [
 someValue,
 someOtherValue,
 theLastValue
 ];

Slide 99

Slide 99 text

@mathias const array = [
 someValue,
 someOtherValue,
 /* more values */,
 theLastValue
 ];

Slide 100

Slide 100 text

@mathias const array = new Array(9001);
 // …
 array[0] = someValue;
 array[1] = someOtherValue;
 // …
 array[9000] = theLastValue;

Slide 101

Slide 101 text

@mathias const array = new Array(9001);
 // " an array with 9001 holes :'(

Slide 102

Slide 102 text

@mathias new Array(n) + allows engines to preallocate space for n elements + optimizes array creation - creates a holey array - slower array operations (compared to packed arrays)

Slide 103

Slide 103 text

@mathias const array = [];
 // …
 array.push(someValue);
 array.push(someOtherValue);
 // …
 array.push(theLastValue);

Slide 104

Slide 104 text

@mathias + creates a packed array (never has any holes in it) + optimizes array operations - engines need to reallocate space as the array grows - slower array creation array = []; array.push(x);

Slide 105

Slide 105 text

Use new Array(n) to optimize the creation of the array by pre-allocating the correct number of elements.

Slide 106

Slide 106 text

Avoid new Array(n) to optimize operations on the array by avoiding holeyness.

Slide 107

Slide 107 text

Write modern, idiomatic JavaScript, and let the JavaScript engine worry about making it fast.

Slide 108

Slide 108 text

Thank you! @mathias // @v8js mths.be/v8ek