Increment and Decrement
This is a quick article to discuss a point brought up in a recent comment. Which is the fastest way to increment: j++, ++j, or j+=1? Likewise, which is the fastest way to decrement? Below I will dispel the myth that there is any difference between them at all.
Here is what the comment claimed:
Also, for what it’s worth I hear (i += 1) is faster than (i++) in ActionScript.
This has been discussed elsewhere, too: FlashKit (no conclusion) and Kirupa (various claims). I don’t know why a decent performance test was never done, but I made one quite easily:
package { import flash.text.*; import flash.utils.*; import flash.display.*; public class IncrementDecrementTest extends Sprite { private var __logger:TextField; public function IncrementDecrementTest() { __logger = new TextField(); __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); const NUM_ITERATIONS:int = 100000000; var i:int; var j:int; var beforeTime:int; log("Increment:"); j = 0; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { j++; } log("\tPost-increment (j++): " + (getTimer()-beforeTime)); j = 0; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { ++j; } log("\tPre-increment (++j): " + (getTimer()-beforeTime)); j = 0; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { j += 1; } log("\tAdd 1 (j+=1): " + (getTimer()-beforeTime)); log("Decrement:"); j = int.MAX_VALUE; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { j--; } log("\tPost-decrement (j--): " + (getTimer()-beforeTime)); j = int.MAX_VALUE; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { --j; } log("\tPre-decrement (--j): " + (getTimer()-beforeTime)); j = int.MAX_VALUE; beforeTime = getTimer(); for (i = 0; i < NUM_ITERATIONS; ++i) { j -= 1; } log("\tSubtract 1 (j-=1): " + (getTimer()-beforeTime)); } private function log(msg:*): void { __logger.appendText(msg + "\n"); } } }
Here are my results:
Environment | Post-Increment | Pre-increment | Add One | Post-Decrement | Pre-Decrement | Subtract One |
---|---|---|---|---|---|---|
3.0 Ghz Intel Core 2 Duo | 236 | 236 | 236 | 236 | 236 | 236 |
There is no difference at all, even with one hundred million operations. So just use whichever you prefer.
#1 by Simon Richardson on March 5th, 2010 ·
I see a massive difference in the Debug player, but this shouldn’t be used for Performance testing, so we can ignore this. I think people forget to switch to normal player when testing this stuff, which is why people see them as a performance difference.
I do get this though running 1000000000 iterations in the standalone (the debug player wouldn’t play this!). As you can see there is absolutely no real difference because the differences are so tight.
Increment:
Post-increment (j++): 2771
Pre-increment (++j): 2778
Add 1 (j+=1): 2814
Decrement:
Post-decrement (j–): 2776
Pre-decrement (–j): 2864
Subtract 1 (j-=1): 2839
#2 by Sindisil on March 5th, 2010 ·
Your test misses an one aspect of the increment and decrement operators: the different semantics of the pre-fix and post-fix versions.
The value of the expression ++i is the value of ‘i’ after increment.
The value of the expression i++ is the value of ‘i’ before it is incremented.
The decrement operator has analogous semantics.
This means that the interpreter, or the code generated by the AVM2 JIT, must make a copy of the original value of the variable operated upon in the post-fix cases. This takes non-zero time.
In the case where the value of the increment or decrement expression is not used, the interpreter or JIT is free to optimize away the saving of the original value.
Changing the increment and decrement test stanzas to include assignment (e.g. change from “j++” to “acc = j++” and “++j” to “acc = ++j”) results in a difference in performance, albeit a minor one
My results on my netbook, running latest FP 10 release player in Firefox:
Increment:
Post-increment (j++): 659
Pre-increment (++j): 644
Decrement:
Post-decrement (j–): 647
Pre-decrement (–j): 584
#3 by jackson on March 5th, 2010 ·
This is a good point. My test only applies to simple increments and decrements where the value of the expression is not used. This is probably most uses though. Nevertheless, thanks for explaining this and providing your test results!
#4 by Sindisil on March 5th, 2010 ·
Yup, the bottom line is, it seldom matters – use the incr/decr form that’s most comfortable.
Frankly, given the many other optimization opportunities that AS3 misses, I was mildly surprised that, when it’s safe to, AVM2 optimizes away the temporary.
It’s also important to note that the difference in performance, even when there is one, is too small to matter in the context of AS3. We’re talking, at most, 50ms over 100,000,000 repetitions.
I just thought it was worth highlighting the different scenarios.
#5 by skyboy on October 24th, 2010 ·
The thing with
a+=1
is that it gets compiled to the same thing as++a
as an optimization. I made a comment on another article about a test for the difference between ++ x2 and +=2 for that reason. And the above post by Sindisil isn’t entirely correct, since how it makes the duplicate is a simple stack operation in both cases (the only difference is the order ofdup
andincrement
), and the speed differences are because other things were happening during the test, and Firefox does a lot of things behind the scenes (on my computer, Firefox will occasionally read/write a few KB from the HDD every 5 seconds throughout its entire run-time).The best way to test flash is on multi-core computers where flash is always put on a dedicated core, that way other computer operations don’t affect flash.
#6 by jackson on October 24th, 2010 ·
I always run tests several times to make sure that the results are consistent across tests, though I only post one sample result set. It’s very common to see fluke results and just keep testing until things settle down to a very minor wiggle.