6

I'm using Math.ceil( Math.abs( x ) ) inside a loop.

Can anyone realize any optimization for this operation? (Bitwise or what?)

You are welcome to benchmark at jsperf.com

Dan
  • 55,715
  • 40
  • 116
  • 154
  • ps: I need the return value of the operation, not the condition it's < or > something – Dan Jan 06 '11 at 12:18
  • I'm afraid it doesn't get any simpler than you've made it. – Nathan MacInnes Jan 06 '11 at 12:25
  • 1
    I've changed the test value to a negative float instead of an integer, so that the code actually has to do something. – OrangeDog Jan 06 '11 at 12:38
  • 1
    From what I can see in WebKit sources, JavaScript methods `Math.abs()` and `Math.ceil()` use C++ cmath functions [`fabs()`](http://www.cplusplus.com/reference/clibrary/cmath/fabs/) and [`ceil()`](http://www.cplusplus.com/reference/clibrary/cmath/ceil/). I really don't think you could optimize that. Are you sure that this is a problem? Could you post code that you're using? – Crozin Jan 06 '11 at 12:50
  • @Crozin: there's overhead is looking up and calling the functions (dynamic dispatch), of which you can get rid of by only using operators (static dispatch) – Christoph Jan 06 '11 at 12:52
  • hi everybody! posted final benchmarking but smth not working yet http://jsperf.com/best-math-ceil-math-abs – Dan Jan 11 '11 at 06:43
  • @Dan - The ones you have posted there do not solve the problem you asked, nor is it clear what you mean by "pixel size". Anyway, I have removed mine: as pointed out it was wrong too. – OrangeDog Jan 12 '11 at 00:11
  • @Crozin: these calculations are used in animation tight loop – Dan Mar 29 '13 at 11:20

6 Answers6

9

Math.abs doesn't get simpler according to webkit JavaScriptCore

case MathObjectImp::Abs:
result = ( arg < 0 || arg == -0) ? (-arg) : arg;

However ceil uses C's ceil function

 case MathObjectImp::Ceil:
    result = ::ceil(arg);

so testing on JSpref http://jsperf.com/math-ceil-vs-bitwise bitwise is faster
testing @orangedog's answer http://jsperf.com/math-ceil-vs-bitwise/2 Math.ceil is faster

So I guess your best choice is:

var n = Math.abs(x);
var f = (n << 0),
f = f == n ? f : f + 1;
Amjad Masad
  • 4,035
  • 1
  • 21
  • 20
3

x < 0 ? Math.ceil(-x) : Math.ceil(x) produces a 40% speedup in Firefox 3.6 (little difference in the others) while remaining relatively readable.

Here is the jsPerf page. Ignore the "some bitwise operators" label; the expression above doesn't use any.

PleaseStand
  • 31,641
  • 6
  • 68
  • 95
  • 1
    I [found with jsperf](http://jsperf.com/two-max-bound-functions) that doing some bitwise stuff is faster than a ceiling function in this case. – starwed Mar 03 '13 at 21:09
2

Javascript isn't a compiled language like C, so bitwise operations that can work wonders in such languages, aren't so great in JS because numbers are stored as 64 bit floating points. Take a look at this SO post.

Even then, what you write in JS will get transformed to native code somehow by underlying browser and it might be faster or slower, depending on implementation.

Since Math.ceil and Math.abs are built in; I'd guess they're heavily optimized, so I doubt you'll be able to get better performance by doing some trickery of your own.

Bottom line: three things stand in your way of doing it faster:

  1. number representation in JS
  2. fact that it's an interpreted language
  3. functions you use are "native", so they should be fast enough on their own
Community
  • 1
  • 1
darioo
  • 46,442
  • 10
  • 75
  • 103
  • 1
    you're forgetting about dispatch overhead - using operators instead of methods will most likely speed up the code – Christoph Jan 06 '11 at 12:59
  • 1
    Although seemingly in JS numbers are all doubles, all JS engines actually store values at integers when possible, so bitwise operators are still quick as there's no conversion needed when the number is already an integer. – gsnedders Jan 16 '11 at 00:34
1

parseInt(Math.abs(x)) + 1 is faster by about 30% on Firefox according to jsperf

As the argument is always positive, the branches in Math.ceil() are unnecessary.

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • 4
    It may or may not be faster but it's also wrong - any number with a zero value fractional component will be incorrectly 1 greater than the ceil'd value. – annakata Jan 06 '11 at 12:43
  • Ah, good point. Checking for this, unsurprisingly, makes it slower again. – OrangeDog Jan 06 '11 at 13:58
1

Here's another one, which doesn't need to do any lookup:

((x >= 0 ? x : -x) + 0.5) >> 0
Christoph
  • 164,997
  • 36
  • 182
  • 240
-2

Two fastest ways to do calculations (giving almost the same speed in modern browsers):

function f (n) {
   return (~~n) + 1;
}

// or 

function f1 (n) {
   return (n | 0) + 1;
}

// some tests, ~~ operator seems to work identicaly on numbers:

( 3.3 | 0 ) === 3;   
( 3.8 | 0 ) === 3;   
( -3.3 | 0 ) === -3; 
( -3.8 | 0 ) === -3;  

unlike Math.floor(-3.3) == Math.floor(-3.8) == -4

Dan
  • 55,715
  • 40
  • 116
  • 154
  • `f1(1); // 2.` These functions don't follow `Math.ceil` behaviour. Perhaps you should accept the top-rated answer, rather than your own. – MikeM May 09 '14 at 20:37