v8 Internals
v8 Internals
@mathias
@mathias
JAVASCRIPT_ENGINE='V8'
@mathias
@mathias
V8 internals
for JavaScript developers
@mathias // @v8js
@mathias
Elements kinds
in V8
@mathias
const array = [1, 2, 3];
@mathias
const array = [1, 2, 3];
// elements kind: PACKED_SMI_ELEMENTS
@mathias
const array = [1, 2, 3];
// elements kind: PACKED_SMI_ELEMENTS
array.push(4.56);
@mathias
const array = [1, 2, 3];
// elements kind: PACKED_SMI_ELEMENTS
array.push(4.56);
// elements kind: PACKED_DOUBLE_ELEMENTS
@mathias
const array = [1, 2, 3];
// elements kind: PACKED_SMI_ELEMENTS
array.push(4.56);
// elements kind: PACKED_DOUBLE_ELEMENTS
array.push('x');
@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
@mathias
Elements kinds
Smi
Doubles
Regular elements
@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
@mathias
array.length; // 5
index 0 1 2 3 4
@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
@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
@mathias
array[8];
// " ???
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false ❌
index 0 1 2 3 4 5 6 7 8 9
@mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false ❌
hasOwnProperty(Array.prototype, '8');
// " false
@mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false ❌
hasOwnProperty(Array.prototype, '8');
// " false ❌
@mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false ❌
hasOwnProperty(Array.prototype, '8');
// " false ❌
hasOwnProperty(Object.prototype, '8');
// " false @mathias
array[8];
// " ??? ❌
hasOwnProperty(array, '8');
// " false ❌
hasOwnProperty(Array.prototype, '8');
// " false ❌
hasOwnProperty(Object.prototype, '8');
// " false ✅ @mathias
array[8];
// " undefined ✅
hasOwnProperty(array, '8');
// " false
hasOwnProperty(Array.prototype, '8');
// " false
hasOwnProperty(Object.prototype, '8');
// " false ✅
@mathias
packedArray[8];
// " undefined ✅
hasOwnProperty(packedArray, '8');
// " true ✅
hasOwnProperty(Array.prototype, '8');
// " false ✅
hasOwnProperty(Object.prototype, '8');
// " false ✅ @mathias
packedArray[8];
// " undefined ✅
hasOwnProperty(packedArray, '8');
// " true ✅
hasOwnProperty(Array.prototype, '8');
// " false ✅
hasOwnProperty(Object.prototype, '8');
// " false ✅ @mathias
array[0];
// " ???
@mathias
array[0];
// " ??? ❌
@mathias
array[0];
// " ??? ❌
@mathias
array[0];
// " ??? ❌
@mathias
array[0];
// " ??? ❌
hasOwnProperty(array, '0');
// " true
@mathias
array[0];
// " ??? ❌
hasOwnProperty(array, '0');
// " true ✅
@mathias
array[0];
// " 1 ✅
hasOwnProperty(array, '0');
// " true ✅
@mathias
PACKED > HOLEY
@mathias
🚀$
PACKED > HOLEY
🐌&
@mathias
Elements kinds
Smi
Doubles
Regular elements
@mathias
Smi, Smi,
packed holey
@mathias
lattice
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 59
Array.prototype.forEach
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 61
Array.prototype.forEach
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.forEach
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.map
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.filter
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.some
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.every
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.reduce
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array.prototype.reduceRight
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Chrome 64
Array#{find,findIndex} '
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
Soon!
Array#{find,findIndex}
PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
HOLEY_SMI_ELEMENTS PACKED_ELEMENTS
HOLEY_DOUBLE_ELEMENTS
HOLEY_ELEMENTS
@mathias
const array = new Array(3);
@mathias
const array = new Array(3);
index 0 1 2
value
@mathias
const array = new Array(3);
index 0 1 2
// HOLEY_SMI_ELEMENTS value
@mathias
const array = new Array(3);
index 0 1 2
array[0] = 'a';
@mathias
const array = new Array(3);
index 0 1 2
array[0] = 'a';
// HOLEY_ELEMENTS
@mathias
const array = new Array(3);
index 0 1 2
array[0] = 'a';
// HOLEY_ELEMENTS
array[1] = 'b';
@mathias
const array = new Array(3);
index 0 1 2
// HOLEY_ELEMENTS
array[1] = 'b';
array[2] = 'c';
@mathias
const array = new Array(3);
index 0 1 2
array[1] = 'b';
array[2] = 'c';
// HOLEY_ELEMENTS (still!)
@mathias
const array = ['a', 'b', 'c'];
// elements kind: PACKED_ELEMENTS
@mathias
const array = ['a', 'b', 'c'];
// elements kind: PACKED_ELEMENTS
// …
array.push(someValue);
array.push(someOtherValue);
@mathias
Avoid holes
Avoid holes!
#ProTip
@mathias
for (let i = 0, item; (item = items[i]) != null; i++) {
doSomething(item);
}
@mathias
for (let i = 0, item; (item = items[i]) != null; i++) {
doSomething(item);
}
@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);
}
@mathias
for (const item of items) {
doSomething(item);
}
@mathias
items.forEach((item) => {
doSomething(item);
});
@mathias
Avoid out-of-bounds reads
Avoid holes!
#ProTip
@mathias
+0 === -0;
// " true
@mathias
+0 === -0;
// " true
Object.is(+0, -0);
// " false
@mathias
[3, 2, 1, +0];
// PACKED_SMI_ELEMENTS
@mathias
[3, 2, 1, +0];
// PACKED_SMI_ELEMENTS
[3, 2, 1, -0];
// PACKED_DOUBLE_ELEMENTS
@mathias
[3, 2, 1, +0];
// PACKED_SMI_ELEMENTS
[3, 2, 1, -0];
// PACKED_DOUBLE_ELEMENTS
[3, 2, 1, NaN, Infinity];
// PACKED_DOUBLE_ELEMENTS
@mathias
Avoid elements kind transitions
Avoid holes!
#ProTip
@mathias
const arrayLike = {};
arrayLike[0] = 'a';
arrayLike[1] = 'b';
arrayLike[2] = 'c';
arrayLike.length = 3;
@mathias
Array.prototype.forEach.call(arrayLike, (value, index) => {
console.log(`${ index }: ${ value }`);
});
// This logs '0: a', then '1: b', and finally '2: c'.
@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'.
@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'.
@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'.
@mathias
Prefer arrays over array-like objects
Avoid holes!
#ProTip
@mathias
$
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
- prototype = 0x313360387f81
- elements = 0x313389e0e4c9 <FixedArray[4]> [HOLEY_SMI_ELEMENTS (COW)]
- length = 4
- properties = 0x3133dae02241 <FixedArray[0]> {
…
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
- prototype = 0x313360387f81
- elements = 0x313389e0e4c9 <FixedArray[4]> [HOLEY_SMI_ELEMENTS (COW)]
- length = 4
- properties = 0x3133dae02241 <FixedArray[0]> {
…
@mathias
$ rlwrap ~/projects/v8/out/x64.debug/d8 --allow-natives-syntax
🐄
- map = 0x3133e0582889 [FastProperties]
- prototype = 0x313360387f81
- elements = 0x313389e0e4c9 <FixedArray[4]> [HOLEY_SMI_ELEMENTS (COW)]
- length = 4
- properties = 0x3133dae02241 <FixedArray[0]> {
…
@mathias
Avoid holes.
— J.K. Rowling
Avoid holes. Avoid out-of-bounds reads.
— Justin Bieber
Avoid holes. Avoid out-of-bounds reads.
Avoid elements kind transitions. Prefer
arrays over array-like objects.
— Albert Einstein
Avoid holes. Avoid out-of-bounds reads.
Avoid elements kind transitions. Prefer
arrays over array-like objects. Eat your
vegetables.
— this slide, just now
One more thing…
@mathias
const array = [
someValue,
someOtherValue,
theLastValue
];
@mathias
const array = [
someValue,
someOtherValue,
/* more values */,
theLastValue
];
@mathias
const array = new Array(9001);
// …
array[0] = someValue;
array[1] = someOtherValue;
// …
array[9000] = theLastValue;
@mathias
const array = new Array(9001);
// " an array with 9001 holes :'(
@mathias
new Array(n)
@mathias
const array = [];
// …
array.push(someValue);
array.push(someOtherValue);
// …
array.push(theLastValue);
@mathias
array = []; array.push(x);
@mathias
Use new Array(n) to optimize the
creation of the array by pre-allocating
the correct number of elements.
Avoid new Array(n) to optimize
operations on the array by avoiding
holeyness.
Write modern, idiomatic JavaScript, and
let the JavaScript engine worry about
making it fast.
Thank you!
@mathias // @v8js
mths.be/v8ek