Search code examples
javascriptperformancealgorithmprofilingprofiler

Why does Math.abs take so much longer than Math.round?


I know Math.abs() and Math.round() are very different functions, but I assumed they would have a relatively similar efficiency.

console.time('round');
for (var i = 0; i < 100000; i++) {

  var a = Math.random() - 0.5;
  Math.round(a);
}
console.timeEnd('round');
console.time('abs');
for (var i = 0; i < 100000; i++) {

  var a = Math.random() - 0.5;
  Math.abs(a);
}
console.timeEnd('abs');

The previous produced these results: round: 136.435ms abs: 4777.983ms

Can anyone explain the radical difference in the timing?

EDIT: When I run the snippit here i get MUCH faster results. Around 2 and 3 ms. Why on earth would it get such radically higher times in a different tab?

Thanks!


Solution

    1. I am not sure you are measuring what you think you are

      The random can really mess things up by cache missing. Also you are substracting by 0.5 which is also FPU operation that is quite more complex then abs itself. I am not JAVA coder but I hope Math.abs(x) is floating point and not integer operation (In C/C++ abs is integer and fabs is floating point). I would create array with random numbers set prior to your loops and then use that in your loops

    2. abs

      abs is just sign bit test + non zero mantissa test. if implementation contains brunch then that can seriously slow thing down. Luckily float/double abs implementation does not need any brunch you just mask out the sign bit (as mantissa is not in 2'os complement for standard IEEE 754 formats).

    3. round

      round is test if MSB of fractional part of mantissa is 1 and if yes then is integer increment applied. so the target is bit shifted to integer and MSB fraction bit is extracted. if implementation contains brunch then that can seriously slow thing down but usually faster is extract the MSB fraction bit to Carry flag and use adc. Still this needs more work than abs and so it should be slower.

    4. so why the results are as are?

      Is your implementation/platform using FPU or software emulation? On FPU the complexity of both operations is almost the same (because the overhead of communication with FPU is usually bigger then such operation itself). On emulation it depends on implementation of the operations and target platform architecture (pipelines,cache control,...)

      my guesses are:

      • abs loop is worse with Cache misses
      • abs implementation is not that much optimized as it could be
      • round is optimized by compiler, sometimes round(x)=floor(a+0.5) and you have a-=0.5; before and as you do not use a for anything else there is a possibility compiler ignores the floor(random-0.5+0.5) and use directly floor(random)

    [notes]

    The times you measured 132ms and 4.7sec are way too big on what HW did you try this? The times from your edit are much more reasonable for common PC HW and code interpreter these days. Did you measure this more then 1 times ?

    If you are trying this in brownser then it could be slowed down by anything in its background (like snipped from different page or still downloading something ...) Also OS can pause the execution but not that much