Why is i=i+1 faster than i++?
Asked Answered
C

5

5

Test this code in Flash:

var i:int = 0;
for (var j:int = 0; j < 5000000; j++)
{
    i=i+1;
}// use about 300ms.

i = 0;
for (var j:int = 0; j < 5000000; j++)
{
    i++;
}// use about 400ms

i = 0;
for (var j:int = 0; j < 5000000; j++)
{
    ++i;
}// use about 400ms too

Why is i=i+1 faster in ActionScript 3 when it's slower in others?

Sorry,I make some mistake.The code above use the same time. but if put it into function,and the result will be different.

var i:int;
var j:int;
var startTime:Number;

function func1():void
{
    i = i + 1;
}

function func2():void
{
    i++;
}

startTime = getTimer();
i = 0;
for (j = 0; j < 10000000; j++)
{
    func1();
}
trace(getTimer() - startTime);//5 times:631,628,641,628,632

startTime = getTimer();
i = 0;
for (j = 0; j < 10000000; j++)
{
    func2();
}
trace(getTimer() - startTime);//5 times:800,814,791,832,777
Cadmus answered 31/8, 2012 at 3:14 Comment(3)
Can you times 10 the loop length, and make sure there really is a difference?Maher
What do you mean by "slower in others?" Other programming languages? Which ones?Lazare
A small side note: It does matter if you have a release-/debugbuild ('permit debugging' setting in FlashIDE), and the results could also vary in a release- and debugplayer. jacksondunstan.com has lots of very in-depth performance tests for several players.Polygnotus
H
5

Where your loop is situated can have a large impact on performance. If your loop is inside a function, Flash will perform calculations using local registers. The loop containing i++ produces thus the following opcodes:

000013 inclocal_i (REG_2)  ; increment i 
000015 inclocal_i (REG_3)  ; increment j
000017 getlocal (REG_3)    ; push j onto stack
000018 pushint 5000000     ; push 5000000 onto stack
000020 iflt -12            ; jump backward if less than

The loop containing i = i + 1 produces the following:

000013 getlocal (REG_2)    ; push i onto stack
000014 pushbyte 1          ; push 1 onto stack
000016 add                 ; add the two
000017 convert_i           ; coerce to integer
000018 setlocal (REG_2)    ; save i back to register 2
000019 inclocal_i (REG_3)  
000021 getlocal (REG_3)
000022 pushint 5000000
000024 iflt -16

i++ is faster than i = i + 1 here since inclocal_i modifies the register directly, without having to load the register onto the stack and saving it back.

The loop becomes be far less efficient when you put it inside a frame script. Flash will store declared variables as class variables. Accessing those requires more work. The i++ loop results in the following:

000017 getlocal (REG_0, this)   ; push this onto stack
000018 dup                      ; duplicate it
000019 setlocal (REG_2)         ; save this to register 2
000020 getproperty i            ; get property "i"
000022 increment_i              ; add one to it
000023 setlocal (REG_3)         ; save result to register 3
000024 getlocal (REG_2)         ; get this from register 2
000025 getlocal (REG_3)         ; get value from register 3
000026 setproperty i            ; set property "i"
000028 kill (REG_3)             ; kill register 2
000030 kill (REG_2)             ; kill register 3
000032 getlocal (REG_0, this)   ; do the same thing with j...
000033 dup
000034 setlocal (REG_2)
000035 getproperty j
000037 increment_i
000038 setlocal (REG_3)
000039 getlocal (REG_2)
000040 getlocal (REG_3)
000041 setproperty j
000043 kill (REG_3)
000045 kill (REG_2)
000047 getlocal (REG_0, this)
000048 getproperty j
000050 pushint 5000000
000052 iflt -40

The i = i + 1 version is somewhat shorter:

000017 getlocal (REG_0, this)   ; push this onto stack
000018 getlocal (REG_0, this)   ; push this onto stack
000019 getproperty i            ; get property "i"
000021 pushbyte 1               ; push 1 onto stack
000023 add                      ; add the two
000024 initproperty i           ; save result to property "i"
000026 getlocal (REG_0, this)   ; increment j...
000027 dup
000028 setlocal (REG_2)
000029 getproperty j
000031 increment_i
000032 setlocal (REG_3)
000033 getlocal (REG_2)
000034 getlocal (REG_3)
000035 setproperty j
000037 kill (REG_3)
000039 kill (REG_2)
000041 getlocal (REG_0, this)
000042 getproperty j
000044 pushint 5000000
000046 iflt -34 
Herodias answered 31/8, 2012 at 15:41 Comment(3)
I like your answer so much better than mine :) how do you get the opcodes did you just modify mm.cfg somehow?Ambiguity
I wrote an AS3 disassembler. You can check it out at: flaczki.net46.net/codedump. Can be hardy for performance optimization.Herodias
Thank you that's awesome! I'm transitioning some to HTML5/JS so I have to lean back on the AS3 stuff for a bit but I'll definitely archive that to check out sometime soon.Ambiguity
A
4

I can't replicate this behavior all three appear to be about the same time for me

Attempt 1
loop 1: 378
loop 2: 396
loop 3: 382

Attempt 2
loop 1: 380
loop 2: 361
loop 3: 361

Attempt 3
loop 1: 375
loop 2: 373
loop 3: 355

Increasing the loops by a factor of 10 I got these times:

Attempt 1
loop 1: 3707
loop 2: 3663
loop 3: 3653

Attempt 2
loop 1: 3722
loop 2: 3632
loop 3: 3711

[TestLoopSpeed.as]

package
{
    import flash.display.Sprite;
    import flash.utils.getTimer;

    public class TestLoopSpeed extends Sprite
    {
        public function TestLoopSpeed()
        {
            var timeNow:Number = getTimer();
            var i:int = 0;

            var startOne:Number = getTimer();
            for (var j:int = 0; j < 5000000; j++)
            {
                i=i+1;
            }
            var endOne:Number = getTimer();


            var startTwo:Number = getTimer();
            i = 0;
            for (var j:int = 0; j < 5000000; j++)
            {
                i++;
            }
            var endTwo:Number = getTimer();

            var startThree:Number = getTimer();
            i = 0;
            for (var j:int = 0; j < 5000000; j++)
            {
                ++i;
            }
            var endThree:Number = getTimer();

            trace("loop 1: " + (endOne - startOne));
            trace("loop 2: " + (endTwo - startTwo));
            trace("loop 3: " + (endThree - startThree));
        }
    }
}

So far as I understand it i++ is ultimately equivalent to i = i+1; with the exception that if an assignment is occurring on that line then the current value of i would be used and a later instruction would add one to i. ++i simply means add 1 to i before doing any other operations on this line. Ultimately I don't think any of these options should really affect performance it appears from every test I've done that the CPU scheduling at any given moment for the flash process is having more of an effect than any given operator.

If there's something wrong with the code I'm using to test please point it out.

Edit

Updated the code to include the while loop option, still not seeing anything that looks like a positive significant difference:

loop 1: 3695
loop 2: 3698
loop 3: 3690
loop 4: 3711

loop 1: 3758
loop 2: 3651
loop 3: 3707
loop 4: 3676

[TestLoopSpeed.as] Updated with fancy while loop pre-increment action

package
{
    import flash.display.Sprite;
    import flash.utils.getTimer;

    public class TestLoopSpeed extends Sprite
    {
        public function TestLoopSpeed()
        {
            var timeNow:Number = getTimer();
            var i:int = 0;

            var startOne:Number = getTimer();
            for (var j:int = 0; j < 50000000; j++)
            {
                i=i+1;
            }
            var endOne:Number = getTimer();


            var startTwo:Number = getTimer();
            i = 0;
            for (var j:int = 0; j < 50000000; j++)
            {
                i++;
            }
            var endTwo:Number = getTimer();

            var startThree:Number = getTimer();
            i = 0;
            for (var j:int = 0; j < 50000000; j++)
            {
                ++i;
            }
            var endThree:Number = getTimer();

            var startFour:Number = getTimer();
            i = 0;
            var j:int = -1;
            while (++j < 50000000)
            {
                ++i;
            }
            var endFour:Number = getTimer();

            trace("loop 1: " + (endOne - startOne));
            trace("loop 2: " + (endTwo - startTwo));
            trace("loop 3: " + (endThree - startThree));
            trace("loop 4: " + (endFour - startFour));
        }
    }
}
Ambiguity answered 31/8, 2012 at 3:22 Comment(0)
A
2

++ and -- operators were designed to resemble the assembly code for increment and decrement, but nowadays it shouldn't make much difference with advancement of compilers. See section increase and decrease

It could be a change in implementation or a temporary regression in actionscript virtual machine.

Abernathy answered 31/8, 2012 at 3:34 Comment(0)
O
0

I don't have a response to your question but the following has been the fastest among all the loops I tried.

import flash.utils.getTimer;

var t:int = getTimer();
var i:int = 0, j:int = -1;
while (++j < 5000000) {
    i += 1;
}
trace(getTimer()-t)

This gives me 83 ms.

Overpass answered 31/8, 2012 at 3:31 Comment(0)
M
0

I suggest instead of i++; use ++i; Because it is faster than both you mentioned.

Look at this good explanation: What is more efficient i++ or ++i?

Monagan answered 31/8, 2012 at 3:35 Comment(1)
It's worth mentioning that a variable will not hold the same value after each of those operations which means they're not interchangeable without altering the code that uses this variable.Shopkeeper

© 2022 - 2024 — McMap. All rights reserved.