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 full-size slide

  2. @mathias
    Elements kinds
    in V8

    View full-size slide

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


    View full-size slide

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

    // elements kind: PACKED_SMI_ELEMENTS


    View full-size slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);


    View full-size slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);

    // elements kind: PACKED_DOUBLE_ELEMENTS

    View full-size slide

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

    // elements kind: PACKED_SMI_ELEMENTS

    array.push(4.56);

    // elements kind: PACKED_DOUBLE_ELEMENTS

    array.push('x');


    View full-size 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');

    // elements kind: PACKED_ELEMENTS

    View full-size slide

  9. @mathias
    Smi
    Doubles
    Regular elements
    Elements kinds

    View full-size slide

  10. @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 full-size slide

  11. @mathias
    array.length; // 5

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

    View full-size slide

  12. @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 full-size slide

  13. @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 full-size slide

  14. @mathias
    array[8];

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

    View full-size 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 full-size slide

  16. @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 full-size 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 full-size slide

  18. @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 full-size 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 full-size slide

  20. @mathias
    array[8];

    // " ??? ❌

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

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

    // " false

    View full-size slide

  21. @mathias
    array[8];

    // " ??? ❌

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

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

    View full-size slide

  22. @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 full-size 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 full-size slide

  24. @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 full-size slide

  25. @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 full-size 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 full-size slide

  27. @mathias
    array[0];

    // " ???


    View full-size slide

  28. @mathias
    array[0];

    // " ??? ❌

    View full-size slide

  29. @mathias
    array[0];

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

    View full-size slide

  30. @mathias
    array[0];

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

    View full-size slide

  31. @mathias
    array[0];

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

    hasOwnProperty(array, '0');

    // " true

    View full-size slide

  32. @mathias
    array[0];

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

    hasOwnProperty(array, '0');

    // " true ✅

    View full-size slide

  33. @mathias
    array[0];

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

    hasOwnProperty(array, '0');

    // " true ✅

    View full-size slide

  34. @mathias
    PACKED > HOLEY

    View full-size slide

  35. @mathias
    PACKED > HOLEY
    #$
    %&

    View full-size slide

  36. @mathias
    Smi
    Doubles
    Regular elements
    Elements kinds

    View full-size slide

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

    View full-size slide

  38. @mathias
    PACKED_SMI_ELEMENTS
    HOLEY_SMI_ELEMENTS
    PACKED_DOUBLE_ELEMENTS
    HOLEY_DOUBLE_ELEMENTS
    PACKED_ELEMENTS
    HOLEY_ELEMENTS

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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


    View full-size slide

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

    View full-size slide

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

    // HOLEY_SMI_ELEMENTS

    index 0 1 2
    value

    View full-size slide

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

    // HOLEY_SMI_ELEMENTS

    array[0] = 'a';

    index 0 1 2
    value 'a'

    View full-size slide

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

    // HOLEY_SMI_ELEMENTS

    array[0] = 'a';

    // HOLEY_ELEMENTS
    index 0 1 2
    value 'a'

    View full-size slide

  55. @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 full-size slide

  56. @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 full-size slide

  57. @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 full-size slide

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

    // elements kind: PACKED_ELEMENTS


    View full-size slide

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

    // elements kind: PACKED_ELEMENTS


    // …

    array.push(someValue);

    array.push(someOtherValue);

    View full-size slide

  60. @mathias
    Avoid holes!
    #ProTip
    Avoid holes

    View full-size slide

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

    doSomething(item);

    }

    View full-size slide

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

    doSomething(item);

    }

    View full-size slide

  63. @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 full-size slide

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

    doSomething(item);

    }

    View full-size slide

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

    doSomething(item);

    });

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    // PACKED_SMI_ELEMENTS


    View full-size slide

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

    // PACKED_SMI_ELEMENTS

    [3, 2, 1, -0];

    // PACKED_DOUBLE_ELEMENTS


    View full-size slide

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

    // PACKED_SMI_ELEMENTS

    [3, 2, 1, -0];

    // PACKED_DOUBLE_ELEMENTS

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

    // PACKED_DOUBLE_ELEMENTS

    View full-size slide

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

    View full-size slide

  73. @mathias
    const arrayLike = {};

    arrayLike[0] = 'a';

    arrayLike[1] = 'b';

    arrayLike[2] = 'c';

    arrayLike.length = 3;

    View full-size slide

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

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

    });

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

    View full-size slide

  75. @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 full-size slide

  76. @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 full-size slide

  77. @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 full-size slide

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

    View full-size slide

  79. @mathias
    $


    View full-size slide

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


    View full-size slide

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


    View full-size slide

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

    V8 version 7.0.244 (candidate)

    d8>

    View full-size slide

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

    V8 version 7.0.244 (candidate)

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

    View full-size slide

  84. @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 full-size slide

  85. @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 full-size 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]; %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 full-size 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);

    DebugPrint: 0x313389e0e551: [JSArray]

    - map = 0x3133e0582889 [FastProperties]

    - prototype = 0x313360387f81

    - elements = 0x313389e0e4c9 [HOLEY_SMI_ELEMENTS (COW)]

    - length = 4

    - properties = 0x3133dae02241 {

    #length: 0x31336c242839 (const accessor descriptor)

    }


    *

    View full-size slide

  88. Avoid holes.
    — J.K. Rowling

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  92. 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 full-size slide

  93. @mathias
    One more thing…

    View full-size slide

  94. @mathias
    const array = [

    someValue,

    someOtherValue,

    theLastValue

    ];

    View full-size slide

  95. @mathias
    const array = [

    someValue,

    someOtherValue,

    /* more values */,

    theLastValue

    ];

    View full-size slide

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

    // …

    array[0] = someValue;

    array[1] = someOtherValue;

    // …

    array[9000] = theLastValue;

    View full-size slide

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

    // " an array with 9001 holes :'(

    View full-size slide

  98. @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 full-size slide

  99. @mathias
    const array = [];

    // …

    array.push(someValue);

    array.push(someOtherValue);

    // …

    array.push(theLastValue);

    View full-size slide

  100. @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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide