Skip to content

Instantly share code, notes, and snippets.

@kevincennis
Last active October 7, 2025 21:46
Show Gist options
  • Select an option

  • Save kevincennis/0cd2138c78a07412ef21 to your computer and use it in GitHub Desktop.

Select an option

Save kevincennis/0cd2138c78a07412ef21 to your computer and use it in GitHub Desktop.

Revisions

  1. kevincennis revised this gist Nov 20, 2021. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -38,6 +38,8 @@ I'd also recommend adding these to your `.zshrc`:

    ## d8 shell examples

    Note: many of these examples have become outdated as `v8` continues to evolve. Hoping to update them in the near future, but for now please be aware that some may not work as expected or may reference optimizations/deoptimizations that no longer happen.

    ### Print optimization stats

    Create `test.js` with the following code:
  2. kevincennis revised this gist Nov 20, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -31,9 +31,9 @@ I'd also recommend adding these to your `.zshrc`:
    *Note*: replace "x64" in these paths with "arm64" on M1 Macs.

    - `$ nano ~/.zshrc`
    - Add `alias d8=/path/to/v8/repo/out.gn/x64.optdebug/d8`
    - Add `alias d8=/path/to/v8/repo/out/x64.optdebug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - Add `export D8_PATH="/path/to/v8/repo/out.gn/x64.optdebug"`
    - Add `export D8_PATH="/path/to/v8/repo/out/x64.optdebug"`
    - `$ source ~/.zshrc`

    ## d8 shell examples
  3. kevincennis revised this gist Nov 20, 2021. 1 changed file with 37 additions and 28 deletions.
    65 changes: 37 additions & 28 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -5,27 +5,36 @@
    - Install Xcode (Avaliable on the Mac App Store)
    - Install Xcode Command Line Tools (Preferences > Downloads)
    - Install [depot_tools](https://www.chromium.org/developers/how-tos/install-depot-tools)
    - `git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git`
    - `sudo nano ~/.bash_profile`
    - Add `export PATH=/path/to/depot_tools:"$PATH"` (it's important that depot_tools comes first here)
    - `source ~/.bash_profile`
    - From the directory you want to install V8 into, run `gclient`
    - `$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git`
    - `$ nano ~/.zshrc`
    - Add `path=('/path/to/depot_tools' $path)`
    - `$ source ~/.zshrc`

    - From the directory you want to install V8 into, run `$ gclient`

    ## Build V8

    - `fetch v8`
    - `cd v8`
    - `gclient sync`
    - `tools/dev/v8gen.py x64.optdebug`
    - `ninja -C out.gn/x64.optdebug` (prepare for lots of fan noise)
    - `$ fetch v8`
    - `$ cd v8`
    - `$ gclient sync`

    For Intel Macs:
    - `$ ./tools/dev/gm.py x64.optdebug`
    - `$ ninja -C out.gn/x64.optdebug`

    For ARM (M1) Macs:
    - `$ ./tools/dev/gm.py arm64.optdebug`
    - `$ ninja -C out.gn/arm64.optdebug`

    I'd also recommend adding these to your `.zshrc`:

    I'd also recommend adding these to your `.bash_profile`:
    *Note*: replace "x64" in these paths with "arm64" on M1 Macs.

    - `sudo nano ~/.bash_profile`
    - `$ nano ~/.zshrc`
    - Add `alias d8=/path/to/v8/repo/out.gn/x64.optdebug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - Add `export D8_PATH="/path/to/v8/repo/out.gn/x64.optdebug"`
    - `source ~/.bash_profile`
    - `$ source ~/.zshrc`

    ## d8 shell examples

    @@ -45,7 +54,7 @@ while ( i++ < 10000 ) {
    }
    ```

    Run `d8 --trace-opt-verbose test.js`
    Run `$ d8 --trace-opt-verbose test.js`

    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for [inline caches](http://en.wikipedia.org/wiki/Inline_caching) -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.

    @@ -65,7 +74,7 @@ while ( i++ < 10000 ) {

    ```

    Run `d8 --trace-opt-verbose test.js`
    Run `$ d8 --trace-opt-verbose test.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different [hidden classes](http://blog.twokul.io/hidden-classes-in-javascript-and-inline-caching/). Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    @@ -85,7 +94,7 @@ while ( i++ < 10000 ) {
    }
    ```

    Run `d8 --trace-opt --trace-deopt test.js`
    Run `$ d8 --trace-opt --trace-deopt test.js`

    You should see that the optimized code for the `test` function was thrown out. What happened here was that V8 kept seeing `test` being passed an object that looked like `{prop: <String>}`. But on the 8000th round of the while loop, we gave it something different. So V8 had to throw away the optimized code, because its initial assumptions were wrong.

    @@ -106,9 +115,9 @@ while ( i++ < 1e7 ) {
    }
    ```

    Run `time d8 --prof test.js` (Generates `v8.log`)
    Run `$ time d8 --prof test.js` (Generates `v8.log`)

    Run `tick-processor` (Reads `v8.log` and `cat`s the parsed output)
    Run `$ tick-processor` (Reads `v8.log` and `cat`s the parsed output)

    This'll show you where the program was spending most of its time, by function. Most of it should be under `LazyCompile: *factorial test.js:1:19`. The asterisk before the function name means that it was optimized.

    @@ -135,13 +144,13 @@ while ( i++ < 1e7 ) {

    ```

    Run `time d8 --prof test.js`
    Run `$ time d8 --prof test.js`

    Run `tick-processor`
    Run `$ tick-processor`

    Roughly the same execution time as the last function, which seems like it should have been faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?

    Run `d8 --trace-inlining test.js`
    Run `$ d8 --trace-inlining test.js`

    Okay. So, we can see that the optimizing compiler was smart here and completely eliminated the overhead of calling both of those functions by inlining them into the optimized code for `factorial`.

    @@ -169,7 +178,7 @@ while ( i++ < 1e5 ) {
    }
    ```

    Run `d8 --trace-gc test.js`
    Run `$ d8 --trace-gc test.js`

    You'll see a bunch of `Scavenge... [allocation failure]`.

    @@ -203,7 +212,7 @@ while ( i++ < 1e5 ) {

    Here, we use a preallocated `ArrayBuffer` and an associated `ArrayBufferView` (in this case a `Uint16Array`) in order to avoid reallocating a new object every time we run `strToArray()`. The result is that we're hardly allocating anything.

    Run `d8 --trace-gc test.js`
    Run `$ d8 --trace-gc test.js`

    Nothing. We never filled up the "new space", so we never had to scavenge.

    @@ -233,7 +242,7 @@ while ( i++ < 1e6 ) {
    }
    ```

    Run `d8 --trace-gc test.js`
    Run `$ d8 --trace-gc test.js`

    Lots of scavenges, which is expected since we're no longer using a preallocated buffer. But there should also be a bunch of `Mark-sweep` lines.

    @@ -243,11 +252,11 @@ Since the frame budget in a web app is about 16ms, you're pretty much guaranteed

    ## Random stuff

    #### `d8 --help` logs all available d8 flags
    #### `$ d8 --help` logs all available d8 flags

    There's a ton there, but you can usually find what you're looking for with something like `d8 --help | grep memory` or whatever.

    #### `d8 --allow-natives-syntax file.js`
    #### `$ d8 --allow-natives-syntax file.js`

    This actually lets you call V8 internal methods from within your JS file, like this:

    @@ -265,7 +274,7 @@ while ( i++ < 1e8 ) {
    }
    ```

    ...and run `d8 --allow-natives-syntax --trace-gc test.js`
    ...and run `$ d8 --allow-natives-syntax --trace-gc test.js`

    Native functions are prefixed with the `%` symbol. A (somewhat incomplete) list of native functions are listed [here](https://github.com/Nathanaela/v8-Natives/blob/master/lib/v8-native-calls.js).

    @@ -298,7 +307,7 @@ b.prop2 = 'baz';
    print( %HaveSameMap( a, b ) );
    ```

    Run `d8 --allow-natives-syntax test.js`
    Run `$ d8 --allow-natives-syntax test.js`

    You should see `true`, then `false`. By adding `b.prop2 = 'baz'`, we modified its structure and created a new hidden class.

  4. kevincennis revised this gist May 25, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -24,7 +24,7 @@ I'd also recommend adding these to your `.bash_profile`:
    - `sudo nano ~/.bash_profile`
    - Add `alias d8=/path/to/v8/repo/out.gn/x64.optdebug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    Add `export D8_PATH="/path/to/v8/repo/out.gn/x64.optdebug"`
    - Add `export D8_PATH="/path/to/v8/repo/out.gn/x64.optdebug"`
    - `source ~/.bash_profile`

    ## d8 shell examples
  5. kevincennis revised this gist May 25, 2018. No changes.
  6. kevincennis revised this gist May 25, 2018. No changes.
  7. kevincennis revised this gist May 25, 2018. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -19,11 +19,12 @@
    - `tools/dev/v8gen.py x64.optdebug`
    - `ninja -C out.gn/x64.optdebug` (prepare for lots of fan noise)

    I'd also recommend adding some aliases to your `.bash_profile`:
    I'd also recommend adding these to your `.bash_profile`:

    - `sudo nano ~/.bash_profile`
    - Add `alias d8=/path/to/v8/repo/out.gn/x64.optdebug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    – Add `export D8_PATH="/path/to/v8/repo/out.gn/x64.optdebug"`
    - `source ~/.bash_profile`

    ## d8 shell examples
  8. kevincennis revised this gist Feb 3, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -56,7 +56,7 @@ function test( obj ) {
    return obj.prop + obj.prop;
    }

    var a = { prop: 'a' }, b = { prop: 1 }, i = 0;
    var a = { prop: 'a' }, b = { prop: [] }, i = 0;

    while ( i++ < 10000 ) {
    test( Math.random() > 0.5 ? a : b );
  9. kevincennis revised this gist Feb 3, 2017. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -44,7 +44,7 @@ while ( i++ < 10000 ) {
    }
    ```

    Run `d8 --trace-opt-verbose file.js`
    Run `d8 --trace-opt-verbose test.js`

    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for [inline caches](http://en.wikipedia.org/wiki/Inline_caching) -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.

    @@ -64,7 +64,7 @@ while ( i++ < 10000 ) {

    ```

    Run `d8 --trace-opt-verbose file.js`
    Run `d8 --trace-opt-verbose test.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different [hidden classes](http://blog.twokul.io/hidden-classes-in-javascript-and-inline-caching/). Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    @@ -77,7 +77,7 @@ function test( obj ) {
    return obj.prop + obj.prop;
    }

    var a = { prop: 'a' }, b = { prop: 1 }, i = 0;
    var a = { prop: 'a' }, b = { prop: [] }, i = 0;

    while ( i++ < 10000 ) {
    test( i !== 8000 ? a : b );
    @@ -140,7 +140,7 @@ Run `tick-processor`

    Roughly the same execution time as the last function, which seems like it should have been faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?

    Run `--trace-inlining test.js`
    Run `d8 --trace-inlining test.js`

    Okay. So, we can see that the optimizing compiler was smart here and completely eliminated the overhead of calling both of those functions by inlining them into the optimized code for `factorial`.

  10. kevincennis revised this gist Feb 3, 2017. 1 changed file with 11 additions and 4 deletions.
    15 changes: 11 additions & 4 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -4,18 +4,25 @@

    - Install Xcode (Avaliable on the Mac App Store)
    - Install Xcode Command Line Tools (Preferences > Downloads)
    - Install [depot_tools](https://www.chromium.org/developers/how-tos/install-depot-tools)
    - `git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git`
    - `sudo nano ~/.bash_profile`
    - Add `export PATH=/path/to/depot_tools:"$PATH"` (it's important that depot_tools comes first here)
    - `source ~/.bash_profile`
    - From the directory you want to install V8 into, run `gclient`

    ## Build V8

    - `git clone [email protected]:v8/v8.git`
    - `fetch v8`
    - `cd v8`
    - `make builddeps`
    - `make x64.debug`
    - `gclient sync`
    - `tools/dev/v8gen.py x64.optdebug`
    - `ninja -C out.gn/x64.optdebug` (prepare for lots of fan noise)

    I'd also recommend adding some aliases to your `.bash_profile`:

    - `sudo nano ~/.bash_profile`
    - Add `alias d8=/path/to/v8/repo/out/x64.debug/d8`
    - Add `alias d8=/path/to/v8/repo/out.gn/x64.optdebug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - `source ~/.bash_profile`

  11. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -269,7 +269,7 @@ d8 doesn't have a `console` object (or a `window` object, for that matter). But

    This is probably my favorite one. I actually *just* found it.

    So in V8, there's this concept of ["hidden classes"](http://www.html5rocks.com/en/tutorials/speed/v8/) (Good explanation a couple paragraphs in). You should read that article – but basically, hidden classes are how V8 (SpiderMonkey and JavaScript Core use similar techniques, too) determine whether or now two objects have the same "shape".
    So in V8, there's this concept of ["hidden classes"](http://www.html5rocks.com/en/tutorials/speed/v8/) (Good explanation a couple paragraphs in). You should read that article – but basically, hidden classes are how V8 (SpiderMonkey and JavaScript Core use similar techniques, too) determine whether or not two objects have the same "shape".

    All things considered, you always want to pass objects of the same hidden class as arguments to functions.

  12. kevincennis revised this gist Aug 15, 2014. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -79,7 +79,7 @@ while ( i++ < 10000 ) {

    Run `d8 --trace-opt --trace-deopt test.js`

    You should see that the optimized code for the `test` function was thrown out. What happened here was that V8 kept seeing `test` being passed an object that looked like `{prop: <Integer>}`. But on the 8000th round of the while loop, we gave it something different. So V8 had to throw away the optimized code, because its initial assumptions were wrong.
    You should see that the optimized code for the `test` function was thrown out. What happened here was that V8 kept seeing `test` being passed an object that looked like `{prop: <String>}`. But on the 8000th round of the while loop, we gave it something different. So V8 had to throw away the optimized code, because its initial assumptions were wrong.


    ### Profiling
    @@ -167,7 +167,7 @@ You'll see a bunch of `Scavenge... [allocation failure]`.

    Basically, V8's GC heap has different "spaces". Most objects are allocated in the "new space". It's super cheap to allocate here, but it's also pretty small (usually somewhere between 1 and 8 MB). Once that space gets filled up, the GC does a "scavenge".

    Scavenging is the fast part of V8 garbage collection. Usually somewhere between 1 and 5ms -- so it might not necessarily cause a noticeable GC pause.
    Scavenging is the fast part of V8 garbage collection. Usually somewhere between 1 and 5ms from what I've seen -- so it might not necessarily cause a noticeable GC pause.

    **Scavenges can only be kicked off by allocations**. If the "new space" never gets filled up, the GC never needs to reclaim space by scavenging.

  13. kevincennis revised this gist Aug 15, 2014. 1 changed file with 29 additions and 0 deletions.
    29 changes: 29 additions & 0 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -265,6 +265,35 @@ Native functions are prefixed with the `%` symbol. A (somewhat incomplete) list

    d8 doesn't have a `console` object (or a `window` object, for that matter). But you can log to the terminal using `print()`.

    #### Comparing Hidden Classes

    This is probably my favorite one. I actually *just* found it.

    So in V8, there's this concept of ["hidden classes"](http://www.html5rocks.com/en/tutorials/speed/v8/) (Good explanation a couple paragraphs in). You should read that article – but basically, hidden classes are how V8 (SpiderMonkey and JavaScript Core use similar techniques, too) determine whether or now two objects have the same "shape".

    All things considered, you always want to pass objects of the same hidden class as arguments to functions.

    Anyway, you can actually *compare* the hidden classes of two objects:

    ```
    function Class( val ) {
    this.prop = val;
    }
    var a = new Class('foo');
    var b = new Class('bar');
    print( %HaveSameMap( a, b ) );
    b.prop2 = 'baz';
    print( %HaveSameMap( a, b ) );
    ```

    Run `d8 --allow-natives-syntax test.js`

    You should see `true`, then `false`. By adding `b.prop2 = 'baz'`, we modified its structure and created a new hidden class.

    #### Node.js

    A lot of these flags (but not all of them) work with Node, too. `--trace-opt`, `--prof`, `--allow-natives-syntax` are all supported.
  14. kevincennis revised this gist Aug 15, 2014. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ while ( i++ < 10000 ) {

    Run `d8 --trace-opt-verbose file.js`

    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for [inline caches](http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers) -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.
    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for [inline caches](http://en.wikipedia.org/wiki/Inline_caching) -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.


    Now modify `test.js` to include the following code:
    @@ -59,7 +59,7 @@ while ( i++ < 10000 ) {

    Run `d8 --trace-opt-verbose file.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different hidden classes. Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.
    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different [hidden classes](http://blog.twokul.io/hidden-classes-in-javascript-and-inline-caching/). Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    ### Print deoptimization stats

    @@ -275,7 +275,7 @@ A list of supported V8 flags can be accessed with `node --v8-options`.

    #### Links

    [Performance Tips for JavaScript in V8](http://www.html5rocks.com/en/tutorials/speed/v8/)
    [Performance Tips for JavaScript in V8](http://www.html5rocks.com/en/tutorials/speed/v8/) (Good basic intro to Hidden Classes)

    [Use forensics and detective work to solve JavaScript performance mysteries](http://www.html5rocks.com/en/tutorials/performance/mystery/)

  15. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -281,7 +281,7 @@ A list of supported V8 flags can be accessed with `node --v8-options`.

    [Breaking the JavaScript Speed Limit with V8](https://www.youtube.com/watch?v=UJPdhx5zTaw)

    [A Tale of Two Compilers](http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers) (Good explanation of Inline Caches)
    [V8 - A Tale of Two Compilers](http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers) (Good explanation of Inline Caches)

    ------

  16. kevincennis revised this gist Aug 15, 2014. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ while ( i++ < 10000 ) {

    Run `d8 --trace-opt-verbose file.js`

    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for "inline caches" -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.
    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for [inline caches](http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers) -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.


    Now modify `test.js` to include the following code:
    @@ -281,6 +281,8 @@ A list of supported V8 flags can be accessed with `node --v8-options`.

    [Breaking the JavaScript Speed Limit with V8](https://www.youtube.com/watch?v=UJPdhx5zTaw)

    [A Tale of Two Compilers](http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers) (Good explanation of Inline Caches)

    ------

    Anyway, this is all still pretty new to me, and there's a lot I haven't figured out yet. But the stuff I've found so far is pretty cool, so I wanted to write something up and share it.
  17. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -261,7 +261,7 @@ while ( i++ < 1e8 ) {

    Native functions are prefixed with the `%` symbol. A (somewhat incomplete) list of native functions are listed [here](https://github.com/Nathanaela/v8-Natives/blob/master/lib/v8-native-calls.js).

    ##### logging
    #### Logging

    d8 doesn't have a `console` object (or a `window` object, for that matter). But you can log to the terminal using `print()`.

  18. kevincennis revised this gist Aug 15, 2014. 1 changed file with 13 additions and 13 deletions.
    26 changes: 13 additions & 13 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,11 @@
    # Installing V8 on a Mac

    ### Prerequisites
    ## Prerequisites

    - Install Xcode (Avaliable on the Mac App Store)
    - Install Xcode Command Line Tools (Preferences > Downloads)

    ### Build V8
    ## Build V8

    - `git clone [email protected]:v8/v8.git`
    - `cd v8`
    @@ -19,9 +19,9 @@ I'd also recommend adding some aliases to your `.bash_profile`:
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - `source ~/.bash_profile`

    ### d8 shell examples
    ## d8 shell examples

    #### Print optimization stats
    ### Print optimization stats

    Create `test.js` with the following code:

    @@ -61,7 +61,7 @@ Run `d8 --trace-opt-verbose file.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different hidden classes. Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    #### Print deoptimization stats
    ### Print deoptimization stats

    Modify the contents of `test.js`:

    @@ -82,7 +82,7 @@ Run `d8 --trace-opt --trace-deopt test.js`
    You should see that the optimized code for the `test` function was thrown out. What happened here was that V8 kept seeing `test` being passed an object that looked like `{prop: <Integer>}`. But on the 8000th round of the while loop, we gave it something different. So V8 had to throw away the optimized code, because its initial assumptions were wrong.


    #### Profiling
    ### Profiling

    Modify `test.js`:

    @@ -139,7 +139,7 @@ Okay. So, we can see that the optimizing compiler was smart here and completely

    The optimized code for both versions ends up being basically identical (which you can check, if you know how to read assembly, by running `d8 --print-opt-code test.js`).

    #### Tracing Garbage Collection
    ### Tracing Garbage Collection

    Modify `test.js`

    @@ -233,13 +233,13 @@ Mark-sweep is the "full" GC. It gets run when the "old space" heap reaches a cer

    Since the frame budget in a web app is about 16ms, you're pretty much guaranteed to drop at least 1 frame every time Mark-sweep runs.

    #### Random stuff
    ## Random stuff

    ###### `d8 --help` logs all available d8 flags
    #### `d8 --help` logs all available d8 flags

    There's a ton there, but you can usually find what you're looking for with something like `d8 --help | grep memory` or whatever.

    ###### `d8 --allow-natives-syntax file.js`
    #### `d8 --allow-natives-syntax file.js`

    This actually lets you call V8 internal methods from within your JS file, like this:

    @@ -261,19 +261,19 @@ while ( i++ < 1e8 ) {

    Native functions are prefixed with the `%` symbol. A (somewhat incomplete) list of native functions are listed [here](https://github.com/Nathanaela/v8-Natives/blob/master/lib/v8-native-calls.js).

    ###### logging
    ##### logging

    d8 doesn't have a `console` object (or a `window` object, for that matter). But you can log to the terminal using `print()`.

    ###### Node.js
    #### Node.js

    A lot of these flags (but not all of them) work with Node, too. `--trace-opt`, `--prof`, `--allow-natives-syntax` are all supported.

    That can be helpful if you want to test something that relies on another library, since you can use Node's `require()`.

    A list of supported V8 flags can be accessed with `node --v8-options`.

    ###### Links
    #### Links

    [Performance Tips for JavaScript in V8](http://www.html5rocks.com/en/tutorials/speed/v8/)

  19. kevincennis revised this gist Aug 15, 2014. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -273,6 +273,14 @@ That can be helpful if you want to test something that relies on another library

    A list of supported V8 flags can be accessed with `node --v8-options`.

    ###### Links

    [Performance Tips for JavaScript in V8](http://www.html5rocks.com/en/tutorials/speed/v8/)

    [Use forensics and detective work to solve JavaScript performance mysteries](http://www.html5rocks.com/en/tutorials/performance/mystery/)

    [Breaking the JavaScript Speed Limit with V8](https://www.youtube.com/watch?v=UJPdhx5zTaw)

    ------

    Anyway, this is all still pretty new to me, and there's a lot I haven't figured out yet. But the stuff I've found so far is pretty cool, so I wanted to write something up and share it.
  20. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -131,7 +131,7 @@ Run `time d8 --prof test.js`

    Run `tick-processor`

    Roughly the same execution time as the last function, which seems like it should be faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?
    Roughly the same execution time as the last function, which seems like it should have been faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?

    Run `--trace-inlining test.js`

  21. kevincennis revised this gist Aug 15, 2014. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -98,9 +98,9 @@ while ( i++ < 1e7 ) {
    }
    ```

    Run `time d8 --prof test.js`
    Run `time d8 --prof test.js` (Generates `v8.log`)

    Run `tick-processor`
    Run `tick-processor` (Reads `v8.log` and `cat`s the parsed output)

    This'll show you where the program was spending most of its time, by function. Most of it should be under `LazyCompile: *factorial test.js:1:19`. The asterisk before the function name means that it was optimized.

    @@ -127,9 +127,9 @@ while ( i++ < 1e7 ) {

    ```

    Run `time d8 --prof test.js` (Generates v8.log)
    Run `time d8 --prof test.js`

    Run `tick-processor` (Reads v8.log and `cat`s the parsed output)
    Run `tick-processor`

    Roughly the same execution time as the last function, which seems like it should be faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?

  22. kevincennis revised this gist Aug 15, 2014. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -99,6 +99,7 @@ while ( i++ < 1e7 ) {
    ```

    Run `time d8 --prof test.js`

    Run `tick-processor`

    This'll show you where the program was spending most of its time, by function. Most of it should be under `LazyCompile: *factorial test.js:1:19`. The asterisk before the function name means that it was optimized.
    @@ -127,6 +128,7 @@ while ( i++ < 1e7 ) {
    ```

    Run `time d8 --prof test.js` (Generates v8.log)

    Run `tick-processor` (Reads v8.log and `cat`s the parsed output)

    Roughly the same execution time as the last function, which seems like it should be faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?
  23. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -61,7 +61,7 @@ Run `d8 --trace-opt-verbose file.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different hidden classes. Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    ### Print deoptimization stats
    #### Print deoptimization stats

    Modify the contents of `test.js`:

  24. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,7 @@ I'd also recommend adding some aliases to your `.bash_profile`:
    - `sudo nano ~/.bash_profile`
    - Add `alias d8=/path/to/v8/repo/out/x64.debug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - 'source ~/.bash_profile`
    - `source ~/.bash_profile`

    ### d8 shell examples

  25. kevincennis revised this gist Aug 15, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion v8.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    - `git clone [email protected]:v8/v8.git`
    - `cd v8`
    - `make builddeps`
    - 'make x64.debug`
    - `make x64.debug`

    I'd also recommend adding some aliases to your `.bash_profile`:

  26. kevincennis created this gist Aug 15, 2014.
    278 changes: 278 additions & 0 deletions v8.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,278 @@
    # Installing V8 on a Mac

    ### Prerequisites

    - Install Xcode (Avaliable on the Mac App Store)
    - Install Xcode Command Line Tools (Preferences > Downloads)

    ### Build V8

    - `git clone [email protected]:v8/v8.git`
    - `cd v8`
    - `make builddeps`
    - 'make x64.debug`

    I'd also recommend adding some aliases to your `.bash_profile`:

    - `sudo nano ~/.bash_profile`
    - Add `alias d8=/path/to/v8/repo/out/x64.debug/d8`
    - Add `alias tick-processor=/path/to/v8/repo/tools/mac-tick-processor`
    - 'source ~/.bash_profile`

    ### d8 shell examples

    #### Print optimization stats

    Create `test.js` with the following code:

    ```js
    function test( obj ) {
    return obj.prop + obj.prop;
    }

    var a = { prop: 'a' }, i = 0;

    while ( i++ < 10000 ) {
    test( a );
    }
    ```

    Run `d8 --trace-opt-verbose file.js`

    You should see that the `test` function was optimized by V8, along with an explanation of why. "ICs" stands for "inline caches" -- and are one of the ways that V8 performs optimizations. Generally speaking, the more "ICs with typeinfo" the better.


    Now modify `test.js` to include the following code:

    ```js
    function test( obj ) {
    return obj.prop + obj.prop;
    }

    var a = { prop: 'a' }, b = { prop: 1 }, i = 0;

    while ( i++ < 10000 ) {
    test( Math.random() > 0.5 ? a : b );
    }

    ```

    Run `d8 --trace-opt-verbose file.js`

    So, you'll see that this time, the `test` function was never actually optimized. And the reason for that is because it's being passed objects with different hidden classes. Try changing the value of `prop` in `a` to an integer and run it again. You should see that the function was able to be optimized.

    ### Print deoptimization stats

    Modify the contents of `test.js`:

    ```js
    function test( obj ) {
    return obj.prop + obj.prop;
    }

    var a = { prop: 'a' }, b = { prop: 1 }, i = 0;

    while ( i++ < 10000 ) {
    test( i !== 8000 ? a : b );
    }
    ```

    Run `d8 --trace-opt --trace-deopt test.js`

    You should see that the optimized code for the `test` function was thrown out. What happened here was that V8 kept seeing `test` being passed an object that looked like `{prop: <Integer>}`. But on the 8000th round of the while loop, we gave it something different. So V8 had to throw away the optimized code, because its initial assumptions were wrong.


    #### Profiling

    Modify `test.js`:

    ```js
    function factorial( n ) {
    return n === 1 ? n : n * factorial( --n );
    }

    var i = 0;

    while ( i++ < 1e7 ) {
    factorial( 10 );
    }
    ```

    Run `time d8 --prof test.js`
    Run `tick-processor`

    This'll show you where the program was spending most of its time, by function. Most of it should be under `LazyCompile: *factorial test.js:1:19`. The asterisk before the function name means that it was optimized.

    Make a note of the execution time that was logged to the terminal. Now try modifying the code to this dumb, contrived example:

    ```js
    function factorial( n ) {
    return equal( n, 1 ) ? n : multiply( n, factorial( --n ) );
    }

    function multiply( x, y ) {
    return x * y;
    }

    function equal( a, b ) {
    return a === b;
    }

    var i = 0;

    while ( i++ < 1e7 ) {
    factorial( 10 );
    }

    ```

    Run `time d8 --prof test.js` (Generates v8.log)
    Run `tick-processor` (Reads v8.log and `cat`s the parsed output)

    Roughly the same execution time as the last function, which seems like it should be faster. You'll also notice that the `multiply` and `equal` functions are nowhere on the list. Weird, right?

    Run `--trace-inlining test.js`

    Okay. So, we can see that the optimizing compiler was smart here and completely eliminated the overhead of calling both of those functions by inlining them into the optimized code for `factorial`.

    The optimized code for both versions ends up being basically identical (which you can check, if you know how to read assembly, by running `d8 --print-opt-code test.js`).

    #### Tracing Garbage Collection

    Modify `test.js`

    ```js
    function strToArray( str ) {
    var i = 0,
    len = str.length,
    arr = new Uint16Array( str.length );
    for ( ; i < len; ++i ) {
    arr[ i ] = str.charCodeAt( i );
    }
    return arr;
    }

    var i = 0, str = 'V8 is the collest';

    while ( i++ < 1e5 ) {
    strToArray( str );
    }
    ```

    Run `d8 --trace-gc test.js`

    You'll see a bunch of `Scavenge... [allocation failure]`.

    Basically, V8's GC heap has different "spaces". Most objects are allocated in the "new space". It's super cheap to allocate here, but it's also pretty small (usually somewhere between 1 and 8 MB). Once that space gets filled up, the GC does a "scavenge".

    Scavenging is the fast part of V8 garbage collection. Usually somewhere between 1 and 5ms -- so it might not necessarily cause a noticeable GC pause.

    **Scavenges can only be kicked off by allocations**. If the "new space" never gets filled up, the GC never needs to reclaim space by scavenging.

    Modify `test.js`:

    ```js
    function strToArray( str, bufferView ) {
    var i = 0,
    len = str.length;
    for ( ; i < len; ++i ) {
    bufferView[ i ] = str.charCodeAt( i );
    }
    return bufferView;
    }

    var i = 0,
    str = 'V8 is the coolest',
    buffer = new ArrayBuffer( str.length * 2 ),
    bufferView = new Uint16Array( buffer );

    while ( i++ < 1e5 ) {
    strToArray( str, bufferView );
    }
    ```

    Here, we use a preallocated `ArrayBuffer` and an associated `ArrayBufferView` (in this case a `Uint16Array`) in order to avoid reallocating a new object every time we run `strToArray()`. The result is that we're hardly allocating anything.

    Run `d8 --trace-gc test.js`

    Nothing. We never filled up the "new space", so we never had to scavenge.

    One more thing to try in `test.js`:

    ```js
    function strToArray( str ) {
    var i = 0,
    len = str.length,
    arr = new Uint16Array( str.length );
    for ( ; i < len; ++i ) {
    arr[ i ] = str.charCodeAt( i );
    }
    return arr;
    }

    var i = 0, str = 'V8 is the coolest', arr = [];

    while ( i++ < 1e6 ) {
    strToArray( str );
    if ( i % 100000 === 0 ) {
    // save a long-term reference to a random, huge object
    arr.push( new Uint16Array( 100000000 ) );
    // release references about 5% of the time
    Math.random() > 0.95 && ( arr.length = 0 );
    }
    }
    ```

    Run `d8 --trace-gc test.js`

    Lots of scavenges, which is expected since we're no longer using a preallocated buffer. But there should also be a bunch of `Mark-sweep` lines.

    Mark-sweep is the "full" GC. It gets run when the "old space" heap reaches a certain size, and it tends to take a lot longer than a scavenge. If you look at the logs, you'll probably see `Scavenge` at around ~1.5ms and `Mark-sweep` closer to 25 or 30ms.

    Since the frame budget in a web app is about 16ms, you're pretty much guaranteed to drop at least 1 frame every time Mark-sweep runs.

    #### Random stuff

    ###### `d8 --help` logs all available d8 flags

    There's a ton there, but you can usually find what you're looking for with something like `d8 --help | grep memory` or whatever.

    ###### `d8 --allow-natives-syntax file.js`

    This actually lets you call V8 internal methods from within your JS file, like this:

    ```js
    function factorial( n ) {
    return n === 1 ? n : factorial( --n );
    }

    var i = 0;

    while ( i++ < 1e8 ) {
    factorial( 10 );
    // run a full Mark-sweep pass every 10MM iterations
    i % 1e7 === 0 && %CollectGarbage( null );
    }
    ```

    ...and run `d8 --allow-natives-syntax --trace-gc test.js`

    Native functions are prefixed with the `%` symbol. A (somewhat incomplete) list of native functions are listed [here](https://github.com/Nathanaela/v8-Natives/blob/master/lib/v8-native-calls.js).

    ###### logging

    d8 doesn't have a `console` object (or a `window` object, for that matter). But you can log to the terminal using `print()`.

    ###### Node.js

    A lot of these flags (but not all of them) work with Node, too. `--trace-opt`, `--prof`, `--allow-natives-syntax` are all supported.

    That can be helpful if you want to test something that relies on another library, since you can use Node's `require()`.

    A list of supported V8 flags can be accessed with `node --v8-options`.

    ------

    Anyway, this is all still pretty new to me, and there's a lot I haven't figured out yet. But the stuff I've found so far is pretty cool, so I wanted to write something up and share it.

    Oh, and I'm sure there's stuff in here that I'm wrong about, because I'm honestly a little out of my depth here. Feedback is appreciated.