bfa*_*tto 10 javascript performance v8
我对这个简单的jsperf测试的结果感到惊讶:
Benchmark.prototype.setup = function() {
var O = function() {
this.f = function(){};
}
var o = new O();
var o2 = {
f : function(){}
};
};
// Test case #1
o.f(); // ~721M ops/s
// Test case #2
o2.f(); // ~135M ops/s
Run Code Online (Sandbox Code Playgroud)
我希望两者都能执行相同的操作(实际上Firefox的性能类似).V8必须优化案例#1,但是什么?
Vya*_*rov 25
关于V8和jsPerf的第一个基础知识:
V8使用一种称为隐藏类的技术.每个隐藏类描述某个对象形状,例如,对象具有x偏移16或对象具有方法的f属性,并且这些隐藏类通过转换连接在一起,因为对象被突变形成转换树(严格来说是dags).并非所有隐藏类都驻留在同一个转换树中; 相反,每个构造函数都会生成一个新的过渡树.看一下这些幻灯片,掌握隐藏课程背后的基本思想.
当jsPerf将执行以下操作来运行测试:给定setup和body它多次生成并运行大约寻找这样的功能:
function measure() {
/* setup */
var start = Date.now();
for (var i = 0; i < N; i++) {
/* body */
}
var end = Date.now();
/* N / (start - end) determines ops / ms reported */
}
Run Code Online (Sandbox Code Playgroud)
每次运行称为样本.
现在让我们看一下基准测试中的过渡树.
隐藏类o属于构造函数中具有根的过渡树O.请注意,每次构造函数都会被调用一次.这允许V8在内存中构建以下转换树:
O{} -f-> O{ f: <closure> }
Run Code Online (Sandbox Code Playgroud)
隐藏的类o本质上告诉V8 o有一个被给定闭包实现的方法f.这允许V8的优化编译器f在上面的基准测试中内联,这实质上使基准测试循环变空.
隐藏类o2属于过渡树Object.首次通知setup被多次调用,因此如果V8尝试将相同的优化应用于f方法,它将到达一个不可能的转换树:
Object{} -f-> Object{ f: <closure> }
-f-> Object{ f: <other closure> }
Run Code Online (Sandbox Code Playgroud)
事实上,V8甚至没有尝试过.V8实现者预见到了这种情况,V8只是做f了一个正常的属性:
Object{} -f-> Object{ f: <property at offset 8> }
Run Code Online (Sandbox Code Playgroud)
因此,要调用o2.f()它需要首先加载它,这也会损害内联.
在这里你应该意识到一件重要的事情:如果你O两次调用构造函数,那么V8将到达V8避免命中的同一个不可能的转换树Object:
O{} -f-> O{ f: <closure> }
-f-> O{ f: <other closure> }
Run Code Online (Sandbox Code Playgroud)
你不能拥有这样的结构.在这种情况下,V8即时转换f为字段而不是使其成为方法并重写转换树:
O{} -f-> O{ f: <property at offset 8> }
Run Code Online (Sandbox Code Playgroud)
在http://jsperf.com/function-call-on-js-objects/3中查看此效果,我new O()在创建之前添加了一个o.您会注意到构造的对象文字和对象的性能new是相同的.
这里的另一个细节是f,如果在全局范围内声明文字,V8也会尝试转换为文字的方法.请参阅http://jsperf.com/function-call-on-js-objects/5和针对V8的问题2246.这里的推理很简单:全局范围内的文字只被评估一次,因此这种推广可能会成功,并且如果多次评估文字,将会出现方法之间的冲突.
| 归档时间: |
|
| 查看次数: |
1413 次 |
| 最近记录: |