pimvdb
pimvdb

Reputation: 154968

How exactly does V8 optimize/inline?

I'm wondering whether it is possible to get knowledge of how exactly V8 optimizes and inlines things.

I created three simple test functions which all calculate the sine of a angle in degrees. I put them all into closures so that V8 should be able to inline the local variables.


1. Using a precalculated constant Math.PI / 180, and then do Math.sin(x * constant).

I used this code:

var test1 = (function() {
  var constant = Math.PI / 180; // only calculate the constant once

  return function(x) {
    return Math.sin(x * constant);
  };
})();

2. Calculating the constant on the fly.

var test2 = (function() {
  var pi = Math.PI; // so that the compiler knows pi cannot change
                    // and it can inline it (Math.PI could change
                    // at any time, but pi cannot)

  return function(x) {
    return Math.sin(x * pi / 180);
  };
})();

3. Using literal numbers and calculating the constant on the fly.

var test3 = (function() {
  return function(x) {
    return Math.sin(x * 3.141592653589793 / 180);
  };
})();

Suprisingly, the results were as follows:

test1 - 25,090,305 ops/sec
test2 - 16,919,787 ops/sec
test3 - 16,919,787 ops/sec

It looks like pi did get inlined in test2 as test2 and test3 result in exactly the same amount of operations per second.

On the other hand, the division does not seem to be optimized (i.e. precalculated), since test1 is significantly faster.

Upvotes: 19

Views: 4946

Answers (2)

thenickdude
thenickdude

Reputation: 1658

To answer your second question, you can see the bytecode that V8 optimized your JS to using this tool: http://mrale.ph/irhydra/2/ . It's fantastic for low-level tuning of code in Chrome.

Upvotes: 4

user395760
user395760

Reputation:

An educated guess at your first question:

Strictly speaking, it can't constant-fold the pi / 180 part, because you don't do pi / 180 in the second and third function. You just divide (x * pi) by 180 (the multiplication has precedence).

Now, you may be asking why it doesn't change the order of operations to wind up with something it can optimize (this process is called reassociation, by the way)... after all, the result is equivalent (a * b / c = (a * b) / c). Math says so, right?

Well, math says so, but math doesn't use floating point numbers. With floats, things are more complicated. x * pi may be rounded, and then the reordering would lead to a different result. The errors will probably be tiny, but still, the leading rule of compiler optimization is: Thou must not change the program's outcome. It's better to perform suboptimal on a few math benchmarks written in an unfortunate way than being off by a pixel (yes, that may be noticeable) in some graphics code.

Upvotes: 7

Related Questions