v8的一些文档翻译



@from
https://gist.github.com/kevincennis/0cd2138c78a07412ef21

Installing V8 on a Mac
先决条件
安装Xcode(可在Mac App Store)
安装Xcode的命令行工具(偏好>下载)
安装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” (重要)
source ~/.bash_profile
进入你想安装V8的目录,运行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)
I’d also recommend adding some aliases 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

source ~/.bash_profile

d8 shell examples

打印优化的数据
创建下面的test.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 test.js

你应该看到测试函数是由V8优化的,以及为什么会被优化的解释。 “IC”代表内联缓存,是V8执行优化的方式之一。 一般来说,“typeinfo”的IC越多越好。

现在修改test.js以包含以下代码:

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

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

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

运行

d8 --trace-opt-verbose test.js

所以,你会看到,这一次,test函数从未被实际优化。 原因是因为它被传递给具有不同隐藏类的对象。 尝试将prop中的值更改为一个整数并再次运行。 您应该会看到该函数能够被优化。

打印去优化统计

修改test.js的内容:

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

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

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

Run

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

您应该看到test函数的优化代码被抛出。 这里V8一直看到测试被传递一个像{prop:}的对象。 但是在8000圈的循环中,我们却操作了不同的东西。 所以V8不得不抛弃优化的代码,因为它的初始假设是错误的。

Profiling

Modify test.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 

(Generates v8.log)

Run

tick-processor 

(Reads v8.log and cats the parsed output)

这将显示程序大部分时间在函数上的消耗。 大部分应该在LazyCompile下:* factorial test.js:1:19。 功能名称前的星号表示已经进行了优化。

记录登录到终端的执行时间。 现在尝试修改代码到这个愚蠢的例子:

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

Run

 tick-processor

与最后一个函数大致相同的执行时间,但是按照我们的想法,这个例子似乎应该更快。 你还会注意到,这个multi和equal的函数在列表中不存在。 奇怪,对吧?

运行d8 –trace-inlining test.js

我们可以看到,优化编译器在这里是很聪明的,并且完全消除了调用这两个函数的开销,它们将它们归结为优化的阶乘代码。两个版本的优化代码最终基本相同(如果您知道如何读取程序集,可以通过运行d8 –print-opt-code test.js来检查)。

Tracing Garbage Collection

Modify test.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].

基本上,V8的GC堆具有不同的“空间”。 大多数对象都分配在“新空间”中。 在这里分配代价超低,但它也很小(通常在1到8 MB之间)。 一旦这个空间被填满,GC就会进行“清理”。

清理是V8垃圾收集的快速部分。 通常从我所看到的介于1到5ms之间 - 所以它可能不一定会引起明显的GC暂停。

清除只能通过分配来启动。 如果“新空间”从未被填满,则GC不需要通过清理来回收空间。

Modify test.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 );
}

在这里,我们使用预分配的ArrayBuffer和相关联的ArrayBufferView(在本例中为Uint16Array),以避免每次运行strToArray()时重新分配一个新对象。 结果是我们几乎没有分配任何东西。

运行d8 –trace-gc test.js

没有。 我们从来没有填补“新空间”,所以我们从来没有去过。

在test.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 );
  }
}

运行d8 –trace-gc test.js

可以看到有许多清除,因为我们不再使用预分配的缓冲区。 但也应该有一堆mark-sweep。

标记扫描(mark-sweep)是“完整”的GC。 当“旧空间”堆达到一定的大小时,它会运行,而且比普通清理时间更长。 如果您查看日志,您可能会看到Scavenge 约1.5ms,Mark-sweep更接近25或30ms。

由于网络应用程序中的帧预算约为16ms,所以每次Mark-sweep运行时,都至少丢弃1帧。

杂项

d8 –help记录所有可用的d8标志

有一大堆文字,但你通常可以找到你想要的东西,像d8 –help | grep 就可以找到你要的东西。

d8 –allow-natives-syntax file.js

这实际上可以让您从JS文件中调用V8内部方法,如下所示:

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 );
}

…并运行d8 –allow-natives-syntax –trace-gc test.js

本机功能前缀为%符号。 这里列出了一些(有些不完整)的本机功能列表。

记录

d8没有控制台对象(或窗口对象)。 但是您可以使用print()。

比较隐藏类

这可能是我最喜欢的。 我其实刚找到它。

所以在V8中,这个概念就是“隐藏的类”(好几个段落的解释)。 你应该阅读这篇文章 - 但是基本上隐藏的类是V8(SpiderMonkey和JavaScript Core也使用类似技术)来确定两个对象是否具有相同的“形状”。

所有考虑的事情,你总是希望将相同隐藏类的对象作为参数传递给函数。

无论如何,您可以实际比较两个对象的隐藏类:

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 ) );

运行d8 –allow-natives-syntax test.js

你应该看到true、false。 通过添加b.prop2 =’baz’,我们修改了它的结构并创建了一个新的隐藏类。

Node.js

很多这些标志(但不是全部)可与Node一起使用。 –trace-opt,–prof,–allow-natives-syntax都支持。

如果您想要测试依赖于另一个库的内容,那么可以使用,因为您可以使用Node的require()。

可以使用node –v8-options选项访问支持的V8标志列表。

Thursday, August 24, 2017 by blast