Inline Math.ceil()
Math.ceil() is a common, mundane function that you likely call all the time. I know I do. If performance gets to be important and you have a Math.ceil() in some inner loop or frequently called function, consider inlining it. Below I’ll show you how and provide a test app showing you just how much CPU time you’ll save.
First of all, you should know how the mathematical ceiling function works. Basically, it just rounds up to the next-highest integer. Since it’s such a simple function, it’s pretty straightforward to implement on our own in AS3, AS2, or JavaScript:
function ceilPositiveOnly(value:Number): Number { return value == int(value) ? value : int(value+1); } function ceil(value:Number): Number { return value == int(value) ? value : value >= 0 ? int(value+1) : int(value); }
In both cases we want to firstly make sure we don’t round up values that are already rounded. Secondly, we want to convert the value to an integer with the int() function, which rounds down, but adjust if need be so it rounds down to the next-highest integer than the input value by simply adding one for positive input values. Here is a little test app to prove that this works:
package { import flash.text.*; import flash.utils.*; import flash.display.*; public class MyApp extends Sprite { private var __logger:TextField; public function MyApp() { __logger = new TextField(); __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); log("Positive only:"); var value:Number; var inlineResult:Number; var ceilResult:Number; for each (value in [1.1, 2.0]) { inlineResult = value == int(value) ? value : int(value+1); ceilResult = Math.ceil(value); log("\t" + value + ": " + inlineResult + " == " + ceilResult + ": " + (inlineResult == ceilResult ? "PASS" : "FAIL")); } log("Positive and negative:"); for each (value in [1.1, 2.0, -1.1, -2.0]) { inlineResult = value == int(value) ? value : value >= 0 ? int(value+1) : int(value); ceilResult = Math.ceil(value); log("\t" + value + ": " + inlineResult + " == " + ceilResult + ": " + (inlineResult == ceilResult ? "PASS" : "FAIL")); } } private function log(msg:*): void { __logger.appendText(msg + "\n"); } } }
And the results are:
Positive only: 1.1: 2 == 2: PASS 2: 2 == 2: PASS Positive and negative: 1.1: 2 == 2: PASS 2: 2 == 2: PASS -1.1: -1 == -1: PASS -2: -2 == -2: PASS
Now for an app to test the speed of inlining our AS3 version of Math.ceil():
package { import flash.text.*; import flash.utils.*; import flash.display.*; public class MyApp extends Sprite { private var __logger:TextField; public function MyApp() { __logger = new TextField(); __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); const ITERATIONS:int = 50000000; var i:int; var res:Number; var before:int = getTimer(); var value:Number = 0.0; for (i = 0; i < ITERATIONS; ++i) { res = value == int(value) ? value : int(value+1); value += 1.0; } log("Inline (positive only): " + (getTimer()-before)); before = getTimer(); value = 0.0; for (i = 0; i < ITERATIONS; ++i) { res = value == int(value) ? value : value >= 0 ? int(value+1) : int(value); value += 1.0; } log("Inline (positive and negative): " + (getTimer()-before)); before = getTimer(); value = 0.0; for (i = 0; i < ITERATIONS; ++i) { res = Math.ceil(value); value += 1.0; } log("Math.ceil(): " + (getTimer()-before)); } private function log(msg:*): void { __logger.appendText(msg + "\n"); } } }
My results on a 2.2 Ghz Intel Core 2 Duo CPU with 2GB RAM on Mac OS X 10.6 are:
Inline (positive only): 479 Inline (positive and negative): 478 Math.ceil(): 3234
Oddly enough, the version supporting negative values seems a hair faster throughout many repetitions of this test. I haven’t yet seen it perform slower than the positive-only version, even though the test only checked positive values so as to have a straight comparison. I therefore don’t see much reason to use the positive-only version other than minor quibbles about readability and code size.
So there you have it. This is a vastly faster (6.76x in AS3) faster way to go about finding the ceiling of numbers in AS3, AS2, and JavaScript. Remember this you find yourself doing something performance critical with a Math.ceil()!
#1 by Ramon Fritsch on October 5th, 2009 ·
Congrats man, very useful tip.
I haven’t idea how much that is costs to cpu. great job.
#2 by aruban on October 5th, 2009 ·
Is that faster than (val >> 0)+1 ?
#3 by aruban on October 5th, 2009 ·
nope. quick test on my macbook Air
Inline (positive only): 1067
Inline (positive and negative): 1067
bitwise operation (value>>0)+1: 698
Math.ceil(): 6484
:)
#4 by jackson on October 5th, 2009 ·
That’s one crazy trick. Where did you find it? I tried it out for correctness:
It seems to round up whole numbers as though you did:
Which I suppose is what the bitwise shift is for. So I suppose if you knew that your input numbers were not whole numbers then it would be usable, but not in the general case.
#5 by Karl Knocking on October 5th, 2009 ·
Maybe he found it here:
http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/
Although the site claims that the casting is faster in AS3..
#6 by aruban on October 5th, 2009 ·
I wrote too quickly, that’s true that (val>>0)+1 works only if u sure that your value is a positive float.
Anyway, made the test and as Karl is mentioning, it looks like the casting is slightly faster that the bit shifting, which surprised me ! :)
——————-
res = value==(value>>0)?value:(value>>0)+1 //positive only
res = value==(value>>0)?value:(value>>0)+(value>=0?1:0); // pos&neg
——————-
Inline:
Positive only:
1.1: 2 == 2: PASS
2: 2 == 2: PASS
Positive and negative:
1.1: 2 == 2: PASS
2: 2 == 2: PASS
-1.1: -1 == -1: PASS
-2: -2 == -2: PASS
BS:
Positive only:
1.1: 2 == 2: PASS
2: 2 == 2: PASS
Positive and negative:
1.1: 2 == 2: PASS
2: 2 == 2: PASS
-1.1: -1 == -1: PASS
-2: -2 == -2: PASS
BW (positive only): 793
Inline (positive only): 758
BW (positive and negative): 778
Inline (positive and negative): 796
Math.ceil(): 7145
——————-
Both are anyway blowing away Math.ceil perf.
As far as I know, for performances, avoiding the use of Math object is part of the good practices :)
#7 by aruban on October 5th, 2009 ·
(this test was done directly on Flash CS3, player 9 with an imac 2.16GHz C2D)
#8 by jackson on October 5th, 2009 ·
Thanks for the follow up. Indeed this does fix the problem and now your solution looks just like mine except for how you do your casting. I was curious so I tried using your bitwise casts in the same test app on the same machine as I did the test in the article. Here’s what I got:
That’s nearly twice as slow as just using the int() cast. This seems to prove that the int() cast is, as Karl pointed out, much quicker than the bitwise trick in AS3. And as you point out, both of them blow away Math.ceil()!
#9 by Patrick Denny on October 28th, 2011 ·
by moving the assignment inside the trinary, you can remove extraneous bitwise shifts (or int() calls) and it speeds up even more
http://wonderfl.net/c/330S
#10 by Patrick Denny on October 28th, 2011 ·
Crap. Munged the code:
and results
If that didn’t work I hope you can get the gist
#11 by benjamin guihaire on November 5th, 2013 ·
I have a faster version, see http://guihaire.com/code/?p=161