Dio*_*ues 1 javascript performance v8 object-literal node.js
在我的程序中,我通常有很多带有签名的函数myFunc({ param })。我想测试每次使用新对象或使用包含对象的变量调用这些函数之间有什么区别。IE:
myFuncA({ param: 1 });
// vs
const paramObj = { param: 1 };
myFuncB(paramObj);
Run Code Online (Sandbox Code Playgroud)
所以我想出了一个简单的测试:
let a = 0;
let b = 0;
function myFuncA({ param }) {
a += param;
}
function myFuncB({ param }) {
b += param;
}
const iterations = 1e9;
console.time('myFuncA');
for(let i = 0; i < iterations; i++) {
myFuncA({ param: 1 });
}
console.timeEnd('myFuncA')
console.time('myFuncB');
const paramObj = { param: 1 };
for(let i = 0; i < iterations; i++) {
myFuncB(paramObj);
}
console.timeEnd('myFuncB');
Run Code Online (Sandbox Code Playgroud)
在此测试中,myFuncA 在我的机器上始终更快,这对我来说是违反直觉的。
myFuncA: 2965.320ms
myFuncB: 4271.787ms
myFuncA: 2956.643ms
myFuncB: 4251.753ms
myFuncA: 2958.409ms
myFuncB: 4269.091ms
myFuncA: 2961.827ms
myFuncB: 4270.164ms
myFuncA: 2957.438ms
myFuncB: 4278.623ms
Run Code Online (Sandbox Code Playgroud)
最初,我假设在函数调用中创建一个对象会使迭代变慢,因为您需要创建该对象,而不是每次都传递相同的对象。但似乎恰恰相反(?)。
我的规格:
为什么会出现这种情况?测试有问题吗?
(这里是 V8 开发人员。)TL;DR:微基准测试很少能成功正确回答您的问题。(将它们放在一起并不是这里的问题,尽管这肯定会导致结果混乱。)
这里的一个直接观察结果是,自 Node 12 以来,情况发生了变化。在当前的 V8 版本(或 Node 14)中,我看到两种情况下的性能几乎相同,“B”仅比“A”慢约 5%。当然,你的问题是为什么 B 更慢。
伯吉的猜测是正确的。V8 可以在执行某些长时间运行的循环时优化函数(这通常是微基准模式,很少出现在实际代码中),并执行所谓的“堆栈上替换”(OSR) 来替换当前正在执行的函数及其优化版本。这显然无法及时返回(重新执行您的代码只想执行一次的事情),因此循环之前发生的任何事情都已经完成并且无法更改。结合内联和逃逸分析,“A”循环被优化为:
for(let i = 0; i < iterations; i++) {
a += 1;
}
Run Code Online (Sandbox Code Playgroud)
而“B”循环被优化为:
for(let i = 0; i < iterations; i++) {
b += paramObj.param;
}
Run Code Online (Sandbox Code Playgroud)
所以你真正测量的是具体化常量1和从对象加载属性之间的区别。
当然,分配对象比不分配对象花费更多时间。也就是说,某些对象分配可能会被优化掉。像这样简单的微基准测试并不能真正告诉您应该如何编写代码以获得最佳性能。
编写高性能代码的有用指南是:
第 1 步:编写对您有意义的代码:易于阅读和理解,易于将来修改/维护。在这个阶段记住算法的复杂性是有用的(例如:对于大于几十个条目的数据集,不要使用二次时间(或更糟糕的)算法(如冒泡排序)),而担心单个机器指令则不然。让引擎负责让事情变得更快。(请注意,这甚至不是特定于 JavaScript;它适用于任何编码。)
步骤 2:如果(且仅当)您发现性能问题,请分析整个应用程序(在尽可能现实的场景中,特别是为其提供代表性输入数据),以确定大部分时间都花在哪里。
第三步:专注于优化那些花费最多时间的部分。有时这可能有点间接;例如:如果您注意到花费了大量时间进行 GC,请查看代码中是否有任何位置分配了许多具有中短生命周期的对象,并考虑避免这些分配。现代 JS 引擎具有非常快的分配器和非常快的 GC,因此在大多数情况下,您不需要特意避免一些对象分配。
| 归档时间: |
|
| 查看次数: |
564 次 |
| 最近记录: |