okor / justice

Embeddable script for displaying web page performance metrics.
MIT License
1.45k stars 56 forks source link

Optimization: don't call Math.floor if already 60 #33

Closed kennyt closed 9 years ago

kennyt commented 9 years ago

Since trackFPS is potentially being called every frame, I believe that not calling Math.floor when it is already 60 can give a fairly non-trivial optimization.

okor commented 9 years ago

I agree that dropping the Math.floor() unless needed should be faster. The execution time difference would likely be miniscule but given the use case it feels totally valid to evaluate efficiency, even on the minute scale. Let's find out :tongue: :candy:

Take a look at this simple test: http://jsperf.com/how-fast-is-math-floor

screen shot 2015-05-20 at 23 12 13

Or if you prefer home brew

var maxTickCount = 10000;

///////////////////////////
// without Math.floor()  //
///////////////////////////
var totalTickCountFast = 0;
var startTimeFast = performance.now();

function tickFast() {
  calcFast();
  function calcFast() {
    totalTickCountFast += 1;
    var fpsClipped = 60;
    if (totalTickCountFast < maxTickCount) calcFast();
  }
}

tickFast();
var fastMessage = "Fast time: " + (performance.now() - startTimeFast);
console.log(fastMessage);

////////////////////////
// with Math.floor()  //
////////////////////////
var totalTickCountSlow = 0;
var startTimeSlow = performance.now();

function tickSlow() {
  calcSlow();
  function calcSlow() {
    totalTickCountSlow += 1;
    var fpsClipped = Math.floor(60);
    if (totalTickCountSlow < maxTickCount) calcSlow();
  }
}

tickSlow();
var slowMessage = "Slow time: " + (performance.now() - startTimeSlow);
console.log(slowMessage);

Result

> Fast time: 0.25599999935366213
> Slow time: 0.3419999993639067

I ran both test types several times each. Each test gives consistent results, relative to the indication of the previous test of the same type. Homebrew says No Floor is faster. jsperf.com indicates the opposite is true which is difficult for me to wrap my head around.

Conclusion: ¯\_(ツ)_/¯

One counter point to making the change, since we are no longer calling Math.floor() every iteration we have now effectively created a scenario where some iterations may be faster or slower than another - given the assumption that an Int (60) takes the same amount of time to .floor() as a relatively long float such as 47.37989197352015 (a value I logged from real usage). This may actually have the net effect of making the "streaming" appear slightly less smooth.

If we made the assumption performing .floor() on a float like 47.37989197352015 would take longer than 60, then that would push the time between iterations even further apart. Thus making the graph stream appear even less smooth.

All that said. I think I just like the code better as it more accurately describes what we are really trying to do. Which is to reduce any weird rendering issues and inefficiencies we may incur by attempting to draw on a canvas element with sub-pixel position precision.

I accept.

kennyt commented 9 years ago

Awesome reply, thank you