简体   繁体   English

JS 性能:function 调用 object 文字 VS object 存储在变量中

[英]JS performance: function call with object literal VS object stored in variable

In my programs I usually have a lot of functions with the signature myFunc({ param }) .在我的程序中,我通常有很多带有签名myFunc({ param })的函数。 I wanted to test what was the difference between calling these functions with a new object every time or with a variable that contains an object. ie:我想测试每次使用新的 object 调用这些函数或使用包含 object 的变量调用这些函数有什么区别。即:

myFuncA({ param: 1 });

// vs

const paramObj = { param: 1 };
myFuncB(paramObj);

So I came up with a simple test:所以我想出了一个简单的测试:

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

In this test, myFuncA is consistently faster on my machine, which is counter intuitive to me.在此测试中,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

Initially I assumed creating an object in the function call would make the iterations slower because you need to create the object, rather than pass the same object every time.最初我假设在 function 调用中创建一个 object会使迭代变慢,因为您需要创建 object,而不是每次都传递相同的 object But it seems to be the other way round (?).但是好像反过来了(?)。

My specs:我的规格:

  • 64bits i7-6500U CPU @ 2.50GHz 64 位 i7-6500U CPU @ 2.50GHz
  • Linux Mint 20 Linux 薄荷20
  • Node v12.14.1节点 v12.14.1

Why does this happen?为什么会这样? Is there something wrong with the test?测试有问题吗?

(V8 developer here.) TL;DR: microbenchmarks rarely succeed in answering your question(s) correctly . (这里是 V8 开发人员。) TL;DR:微基准测试很少能成功地正确回答您的问题 (Putting two of them together is not the problem here, though it certainly can be a reason for confusing results.) (将它们中的两个放在一起不是这里的问题,尽管它肯定导致混淆结果。)

One immediate observation here is that things have changed since Node 12. With current V8 versions (or Node 14), I'm seeing nearly the same performance in both cases, with "B" only being ~5% slower than "A".这里的一个直接观察是,自 Node 12 以来情况发生了变化。对于当前的 V8 版本(或 Node 14),我看到两种情况下的性能几乎相同,“B”仅比“A”慢 5%。 Of course, your question is why B is slower at all.当然,你的问题是为什么 B 慢一点。

Bergi's guess is spot on. Bergi 的猜测是正确的。 V8 can optimize functions while some long-running loop is executing (which is typically a microbenchmark pattern, rarely showing up in real-world code) and perform so-called "on-stack replacement" (OSR) to replace the currently-executing function with its optimized version. V8 可以在执行一些长时间运行的循环时优化函数(这通常是微基准测试模式,很少出现在现实世界的代码中)并执行所谓的“堆栈替换”(OSR)来替换当前正在执行的 function及其优化版本。 This obviously can't go back in time (to re-execute things your code only wants to execute once), so anything that happened before the loop is a done deal and can't be changed.这显然不能 go 及时返回(重新执行你的代码只想执行一次的事情),所以在循环之前发生的任何事情都是既定的并且不能改变。 Combined with inlining and escape analysis, the "A" loop is optimized to:结合内联和逃逸分析,“A”循环被优化为:

for(let i = 0; i < iterations; i++) {
    a += 1;
}

whereas the "B" loop is optimized to:而“B”循环被优化为:

for(let i = 0; i < iterations; i++) {
    b += paramObj.param;
}

so what you're really measuring is the difference between materializing a constant 1 and loading a property from an object.所以你真正测量的是实现常量1和从 object 加载属性之间的区别。

Of course, allocating an object takes more time than not allocating an object. That said, some object allocations might get optimized away.当然,分配 object 比不分配 object 花费更多时间。也就是说,一些 object 分配可能会被优化掉。 So simple microbenchmarks like this one can't really tell you how you should be writing your code for best performance.如此简单的微基准测试无法真正告诉您应该如何编写代码以获得最佳性能。

Useful guidelines for writing high-performance code are:编写高性能代码的有用指南是:

Step 1: Write code that makes sense to you: is easy to read and understand, easy to modify/maintain in the future.第 1 步:编写对您有意义的代码:易于阅读和理解,便于将来修改/维护。 Keeping algorithmic complexity in mind at this stage is useful (eg: don't use quadratic-time (or worse) algorithms (like bubble-sort) for data sets larger than a few dozen entries), worrying about individual machine instructions is not.在这个阶段记住算法的复杂性是有用的(例如:不要对大于几十个条目的数据集使用二次时间(或更糟的)算法(如冒泡排序)),担心单个机器指令不是。 Let the engine take care of making things fast.让引擎负责让事情变得更快。 (Note that this isn't even specific to JavaScript; it applies to any coding.) (请注意,这甚至不特定于 JavaScript;它适用于任何编码。)

Step 2: If (and only if) you are perceiving performance issues, profile the whole application (in as realistic a scenario as possible, in particular feeding it representative input data), to determine where most time is being spent.第 2 步:如果(且仅当)您察觉到性能问题,分析整个应用程序(在尽可能真实的场景中,特别是为其提供代表性输入数据),以确定大部分时间花在了哪里。

Step 3: Focus on optimizing precisely those parts where most time is being spent.第 3 步:专注于精确优化那些花费最多时间的部分。 Sometimes this can be a bit indirect;有时这可能有点间接; for example: if you notice that a lot of time is spent doing GC, see if there are any places in the code that allocate many objects with short-to-medium lifetime, and think about avoiding those allocations.例如:如果您注意到很多时间花在了 GC 上,请查看代码中是否有任何地方分配了许多具有中短生命周期的对象,并考虑避免这些分配。 Modern JS engines have very fast allocators and very fast GCs, so in most cases, you don't need to go out of your way to avoid a few object allocations.现代 JS 引擎具有非常快的分配器和非常快的 GC,因此在大多数情况下,您不需要go 来避免一些 object 分配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM