Upgrade to Pro — share decks privately, control downloads, hide ads and more …

V8 internals for JavaScript developers

V8 internals for JavaScript developers

This presentation demonstrates how learning just a little bit about JavaScript engine internals can help you improve the run-time performance of your JavaScript code — not just in V8 specifically, but across all JavaScript engines!

Video: TBD

A written version is available here: https://mths.be/v8ek

Mathias Bynens

August 24, 2018
Tweet

More Decks by Mathias Bynens

Other Decks in Technology

Transcript

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

    View Slide

  2. @mathias

    View Slide

  3. @mathias
    Elements kinds
    in V8

    View Slide

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


    View Slide

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

    // elements kind: PACKED_SMI_ELEMENTS


    View Slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);


    View Slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);

    // elements kind: PACKED_DOUBLE_ELEMENTS

    View Slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);

    // elements kind: PACKED_DOUBLE_ELEMENTS

    array.push('x');


    View Slide

  9. @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

    View Slide

  10. @mathias
    Smi
    Doubles
    Regular elements
    Elements kinds

    View Slide

  11. @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

    View Slide

  12. @mathias
    array.length; // 5

    index 0 1 2 3 4
    value 1 2 3 4.56 'x'

    View Slide

  13. @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

    View Slide

  14. @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

    View Slide

  15. @mathias
    array[8];

    // " ???
    index 0 1 2 3 4 5 6 7 8 9
    value 1 2 3 4.56 'x' 1

    View Slide

  16. @mathias
    array[8];

    // " ??? ❌
    index 0 1 2 3 4 5 6 7 8 9
    value 1 2 3 4.56 'x' 1

    View Slide

  17. @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

    View Slide

  18. @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

    View Slide

  19. @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

    View Slide

  20. @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

    View Slide

  21. @mathias
    array[8];

    // " ??? ❌

    8 >= 0 && 8 < array.length; // bounds check
    // " true ❌
    hasOwnProperty(array, '8');

    // " false ❌
    hasOwnProperty(Array.prototype, '8');

    // " false

    View Slide

  22. @mathias
    array[8];

    // " ??? ❌

    8 >= 0 && 8 < array.length; // bounds check
    // " true ❌
    hasOwnProperty(array, '8');

    // " false ❌
    hasOwnProperty(Array.prototype, '8');
    // " false ❌

    View Slide

  23. @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

    View Slide

  24. @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 ✅

    View Slide

  25. @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 ✅

    View Slide

  26. @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 ✅

    View Slide

  27. @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 ✅

    View Slide

  28. @mathias
    array[0];

    // " ???


    View Slide

  29. @mathias
    array[0];

    // " ??? ❌

    View Slide

  30. @mathias
    array[0];

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

    View Slide

  31. @mathias
    array[0];

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

    View Slide

  32. @mathias
    array[0];

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

    hasOwnProperty(array, '0');

    // " true

    View Slide

  33. @mathias
    array[0];

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

    hasOwnProperty(array, '0');

    // " true ✅

    View Slide

  34. @mathias
    array[0];

    // " 1 ✅
    0 >= 0 && 0 < array.length; // bounds check
    // " true

    hasOwnProperty(array, '0');

    // " true ✅

    View Slide

  35. @mathias
    PACKED > HOLEY

    View Slide

  36. @mathias
    PACKED > HOLEY
    #$
    %&

    View Slide

  37. @mathias
    Smi
    Doubles
    Regular elements
    Elements kinds

    View Slide

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

    View Slide

  39. lattice

    View Slide

  40. @mathias
    PACKED_SMI_ELEMENTS
    HOLEY_SMI_ELEMENTS
    PACKED_DOUBLE_ELEMENTS
    HOLEY_DOUBLE_ELEMENTS
    PACKED_ELEMENTS
    HOLEY_ELEMENTS

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. @mathias

    View Slide

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

    View Slide

  53. @mathias
    const array = new Array(3);


    View Slide

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

    View Slide

  55. @mathias
    const array = new Array(3);

    // HOLEY_SMI_ELEMENTS

    index 0 1 2
    value

    View Slide

  56. @mathias
    const array = new Array(3);

    // HOLEY_SMI_ELEMENTS

    array[0] = 'a';

    index 0 1 2
    value 'a'

    View Slide

  57. @mathias
    const array = new Array(3);

    // HOLEY_SMI_ELEMENTS

    array[0] = 'a';

    // HOLEY_ELEMENTS
    index 0 1 2
    value 'a'

    View Slide

  58. @mathias
    const array = new Array(3);

    // HOLEY_SMI_ELEMENTS

    array[0] = 'a';

    // HOLEY_ELEMENTS
    array[1] = 'b';

    index 0 1 2
    value 'a' 'b'

    View Slide

  59. @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! (

    View Slide

  60. @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'

    View Slide

  61. @mathias
    const array = ['a', 'b', 'c'];

    // elements kind: PACKED_ELEMENTS


    View Slide

  62. @mathias
    const array = ['a', 'b', 'c'];

    // elements kind: PACKED_ELEMENTS


    // …

    array.push(someValue);

    array.push(someOtherValue);

    View Slide

  63. @mathias
    Avoid holes!
    #ProTip
    Avoid holes

    View Slide

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

    doSomething(item);

    }

    View Slide

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

    doSomething(item);

    }

    View Slide

  66. @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);

    }

    View Slide

  67. @mathias
    for (const item of items) {

    doSomething(item);

    }

    View Slide

  68. @mathias
    items.forEach((item) => {

    doSomething(item);

    });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. @mathias
    [3, 2, 1, +0];

    // PACKED_SMI_ELEMENTS


    View Slide

  73. @mathias
    [3, 2, 1, +0];

    // PACKED_SMI_ELEMENTS

    [3, 2, 1, -0];

    // PACKED_DOUBLE_ELEMENTS


    View Slide

  74. @mathias
    [3, 2, 1, +0];

    // PACKED_SMI_ELEMENTS

    [3, 2, 1, -0];

    // PACKED_DOUBLE_ELEMENTS

    [3, 2, 1, NaN, Infinity];

    // PACKED_DOUBLE_ELEMENTS

    View Slide

  75. @mathias
    Avoid holes!
    #ProTip
    Avoid elements kind transitions

    View Slide

  76. @mathias
    const arrayLike = {};

    arrayLike[0] = 'a';

    arrayLike[1] = 'b';

    arrayLike[2] = 'c';

    arrayLike.length = 3;

    View Slide

  77. @mathias
    Array.prototype.forEach.call(arrayLike, (value, index) => {

    console.log(`${ index }: ${ value }`);

    });

    // This logs '0: a', then '1: b', and finally '2: c'.

    View Slide

  78. @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'.

    View Slide

  79. @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'.

    View Slide

  80. @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'.

    View Slide

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

    View Slide

  82. @mathias
    $


    View Slide

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


    View Slide

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


    View Slide

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

    V8 version 7.0.244 (candidate)

    d8>

    View Slide

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

    V8 version 7.0.244 (candidate)

    d8> const array = [1, 2,, 3];

    View Slide

  87. @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);


    View Slide

  88. @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)

    }


    View Slide

  89. @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)

    }


    View Slide

  90. @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)

    }


    *

    View Slide

  91. View Slide

  92. Avoid holes.
    — J.K. Rowling

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  97. @mathias
    One more thing…

    View Slide

  98. @mathias
    const array = [

    someValue,

    someOtherValue,

    theLastValue

    ];

    View Slide

  99. @mathias
    const array = [

    someValue,

    someOtherValue,

    /* more values */,

    theLastValue

    ];

    View Slide

  100. @mathias
    const array = new Array(9001);

    // …

    array[0] = someValue;

    array[1] = someOtherValue;

    // …

    array[9000] = theLastValue;

    View Slide

  101. @mathias
    const array = new Array(9001);

    // " an array with 9001 holes :'(

    View Slide

  102. @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)

    View Slide

  103. @mathias
    const array = [];

    // …

    array.push(someValue);

    array.push(someOtherValue);

    // …

    array.push(theLastValue);

    View Slide

  104. @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);

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide