connectedpixel.com

actionscript, web development

AS2: Static vs. Instance vs. Setter Funcs

Submitted by joelmay on 7 September, 2005 - 1:22pm.

Flash has several ways to define a function:

  • Class methods
  • Setter/getter methods
  • Static class methods
  • Local functions

I wrote equivalent functions in each form and compared their performance. The raw data is down below in the appendix. I'll go over the results here that I find surprising:

Setter/Getters vs. Regular Instance Functions

// Setter/Getter
p.XVal = 23.4;        // 25 msec / 1000
x = p.XVal;           // 12 msec / 1000

vs.

p.setX(23.4);         // 13 msec / 1000
x = p.getX();         // 12 msec / 1000

As you can see, the equivalent getter and getX function have the same performance, but the setter is much slower than setX(). I have no idea why. Someday I'll have to flasm it to see what's going on.

Static vs. Regular Instance Functions

// Static
Point.staticSetX(p,23.4);   // 22 msec / 1000
x = Point.staticGetX(p);    // 21 msec / 1000

vs.

// Instance 
p.setX(23.4);         // 13 msec / 1000
x = p.getX();         // 12 msec / 1000

I was expecting the static functions to be faster. But I was wrong. They're much slower.

Local Functions using a Closure

// Local.  Not using closure.
var f:Function = function(p:Point):Number 
{
    return p.x; 
}
var p:Point = new Point(23.4, 56.7);
for (var i:Number = 0 ; i < 1000 ; i++){
    var x:Number = f(p);           // 14 msec / 1000
}

vs.

var p:Point = new Point(23.4, 56.7);
        
var f:Function = 
 function():Number { return p.x; }
            
for (var i:Number = 0 ; i < 1000 ; i++){
  var x:Number = f();            // 25 msec / 1000
} 

A "closure" is the local variable stack in the enclosing function. Somehow, a locally defined function can use this. But it's more expensive than simply passing the local variable as an argument.

Note: oddhammer.com has many more perfomance test results. The focus of this article and the previous article has been AS2 specific features.


Appendix

You can run the tests on your computer. This was built for Flash 7.



Here's the code being tested. Notice that each function has a line in the above Flash movie (assuming you ran the test).

import com.connectedpixel.apps.perftest.Point;

class com.connectedpixel.apps.perftest.FuncTypeCompare {
    
    static public function setViaSetters():Void {
        var p:Point = new Point();
        for (var i:Number = 0 ; i < 1000 ; i++){
            p.XVal = 23.4;
        }
    }
    static public function setViaFunc():Void {
        var p:Point = new Point();
        for (var i:Number = 0 ; i < 1000 ; i++){
            p.setX(23.4);
        }
    }
    
    static public function setViaStatic():Void {
        var p:Point = new Point();
        for (var i:Number = 0 ; i < 1000 ; i++){
            Point.staticSetX(p,23.4);
        }
    }
    
    static public function setViaLocal():Void {
        
        var f:Function = 
            function(p:Point,x1:Number):Void 
            { 
                p.x = x1; 
            }
            
        var p:Point = new Point(23.4, 56.7);
        for (var i:Number = 0 ; i < 1000 ; i++){
            f(p,23.4);
        }
    }

    static public function setViaLocalClosure():Void {
        var p:Point = new Point(23.4, 56.7);
        
        var f:Function = function(x1:Number):Void 
            { p.x = x1; }
            
        for (var i:Number = 0 ; i < 1000 ; i++){
            f(23.4);
        }
    }
    
    static public function getViaGetter():Void {
        var p:Point = new Point();
        p.x = 23.4;
        for (var i:Number = 0 ; i < 1000 ; i++){
            var x:Number = p.XVal;
        }
    }
    static public function getViaFunc():Void {
        var p:Point = new Point(23.4, 56.7);
        for (var i:Number = 0 ; i < 1000 ; i++){
            var x:Number = p.getX();
        }
    }
    static public function getViaStatic():Void {
        
        var p:Point = new Point(23.4, 56.7);
        
        for (var i:Number = 0 ; i < 1000 ; i++){
            var x:Number = Point.staticGetX(p);
        }
    }
    static public function getViaLocal():Void {
        
        var f:Function = function(p:Point):Number 
        {
            return p.x; 
        }
        
        var p:Point = new Point(23.4, 56.7);
        for (var i:Number = 0 ; i < 1000 ; i++){
            var x:Number = f(p);
        }
    }
    static public function getViaLocalClosure():Void {
        var p:Point = new Point(23.4, 56.7);
        
        var f:Function = 
            function():Number { return p.x; }
            
        for (var i:Number = 0 ; i < 1000 ; i++){
            var x:Number = f();
        }
    }

}

Here's the Point class that's used in the above code.

class com.connectedpixel.apps.perftest.Point  {

    public var x:Number;
    public var y:Number;
    
    public function Point(x0:Number, y0:Number)
    {
        x = x0; y = y0;
    }

    public function set(x1:Number, y1:Number):Void
    {
        x = x1;  y = y1;
    }

    public function set XVal(x1:Number):Void { x = x1; }
    public function set YVal(y1:Number):Void { y = y1; }
    public function get XVal():Number { return x; }
    public function get YVal():Number { return y; }
    
    public function getX():Number { return x; }
    public function setX(x1:Number):Void { x = x1; }
    
    static public function staticGetX(p:Point):Number 
    { 
        return p.x; 
    }
    static public function staticSetX(p:Point,x1:Number):Void 
    { 
        p.x = x1; 
    }
}
Anonymous (not verified) Says:

re: Local Functions using a Closure

10 October, 2005 - 11:36pm

Is it really that odd that the performance would be that much slower? I could be wrong, but passing it as an argument should enable better use of registers. Please tell me if I am wrong since I don't like to make the same mistake twice : )

joelmay Says:

I don't know how the Flash VM works

13 October, 2005 - 11:57am

I don't know anything about the Flash VM. I don't know how it uses its virtual registers.

In C, local variables and arguments are on the stack by default, but some optimizers can use registers instead.

My guess now is that when the variables is accessed in the local function, the VM first searches the local stack for the variable, then, when it doesn't find it, it searches the closure object. So, using a argument variable rather than a closure variable would require less searching and be faster.

Of course, this is just a guess. Flasm would give a better idea. But that might now even tell us. It might be necessary to disassemble the Flash player itself to understand. But that requires more curiosity and energy than I have.

cyriel (not verified) Says:

contrary to your articel from 1. Sept.

9 February, 2006 - 2:39am

Hi there,

interesting article.

But isn't there a contrary between your point here

// Setter/Getter
p.XVal = 23.4; // 25 msec / 1000
x = p.XVal; // 12 msec / 1000

vs.

p.setX(23.4); // 13 msec / 1000
x = p.getX(); // 12 msec / 1000

and your statement in

http://www.connectedpixel.com/blog/performance/impact/oo

where you present the following


// Recycle the point. Use a function to set the values.
p.set(1.2,13.4); // 15 msec / 1000

vs.

// Recycle the point and set the values directly.
p.x = 1.2; p.y = 3.4; // 4 msec / 1000

joelmay Says:

Actually, they are very

9 February, 2006 - 7:18am

Actually, they are very different, although they look the same. The top test is using a setter and a getter function. An implicit function call is made when the value is set or get:

public function set XVal(x:Number):Void {
   _x = x;
}
public function get XVal():Number { 
   return _x;
}

These functions are called automatically when you call p.XVal = 123 and a = p.XVal; I find it interesting that this is slower than similar functions that are not setter/getter --

public function setX(x:Number):Void { _x = x; }
public function getX():Number { return _x; }

These 2 functions look very similar to the top to, yet the top two are much slower.

The bottom code above:

p.x = 1.2;

is not calling a function. The virtual machine is doing less work. The "x" property on the p object is being set directly.

The overall message is that getter/setter functions can make your code more safe (you can add checks, etc. to the getter/setters), but you pay a penalty timewise.

Dave (not verified) Says:

It's slower because....

3 April, 2007 - 8:13am

To quote your results:
// Setter/Getter
p.XVal = 23.4; // 25 msec / 1000
x = p.XVal; // 12 msec / 1000

vs.

p.setX(23.4); // 13 msec / 1000
x = p.getX(); // 12 msec / 1000

-----
The reason the setter is slower, is because the setter actually calls the getter first, which is quite peculiar really.

Regards,

Dave.

joelmay Says:

I didn't know that

3 April, 2007 - 8:22am

That is weird. You wouldn't think it would do that.
Thanks, Joel