简体   繁体   中英

Does the line which never runs affect performance in V8?

I'd like to know the reason why these two codes run at different speeds.

fast.js

var N = 2e9;
(function () {
  console.time();
  var count = 0;
  for (var i = 0; i < N; i++) count++;
  if(false) console.log(count);
  console.timeEnd()
})();

slow.js

var N = 2e9;
(function () {
  console.time();
  var count = 0;
  for (var i = 0; i < N; i++) count++;
  if(false) (() => console.log(count));
  console.timeEnd()
})();

Execution

% node fast.js 
default: 1.297s 
% node slow.js 
default: 3.175s 

The difference between the codes is line 6.

if(false) console.log(count);

if(false) (() => console.log(count));

But these parts never run because it's dead code.

@tkihira analyzed and concluded that variable count was allocated in heap because it's used in closure when slow.js ran so that it took more time to increment it in the for loop. While count in fast.js is in a register.

This made the speed difference.

Does anyone know which part of the code in V8 makes this behavior? Is it true that a number variable is allocated in heap when it's used in a closure that never runs and a variable and it's in a register when it's not used in a closure?

Analytics by @tkihira : source code: https://gist.github.com/tkihira/b9c7fe8c31e21f1022011e377f7e672d

jit output: https://gist.github.com/tkihira/67a07231cc6a4ce55462d8cdfa51a6e2

diff: https://gist.github.com/tkihira/5f5877cde581c66cae3bc02f88e505e4

twitter thread: https://twitter.com/tkihira/status/1365280241195782149

(V8 developer here.)

Does anyone know which part of the code in V8 makes this behavior?

It's part of the "scope resolution" system, and most of the code is in src/ast/scopes.cc . (It's fairly complicated, because there are so many things that it has to deal with.)

Is it true that a number variable is allocated in heap when it's used in a closure that never runs and a variable and it's in a register when it's not used in a closure?

Yes.

It doesn't matter whether the variable contains a number or anything else.

It doesn't matter how often the closure will run, including whether it will run at all. At the time where the decision needs to be made where to allocate variables, there is no information available yet about what will or won't execute. (Also, closures that are statically guaranteed to never execute are so rare in real code that it most likely wouldn't be worth optimizing for them even if it was possible.)

Variables used by closures are "context-allocated", which makes accessing them a little bit slower than "stack-allocated" variables. If/when the function in question gets optimized, stack-allocated variables may or may not end up being kept in a register some or all of the time, depending on what other register-allocation decisions the optimizing compiler makes.

In most real-world situations, you won't notice the difference (so this is usually not something worth worrying about), but a microbenchmark like a loop that only does count++ makes it observable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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