Beware of Getters and Setters
Getters and setters are indeed a very nice feature of AS3. They eliminate a lot of typing (.x versus .getX()) shaves off five characters and removes the need to hit the shift key for X) and make getting and setting values much more natural by disguising the fact that you’re actually calling a function. The downsides include the difficulty (impossibility?) of getting a Function variable for them and lower performance. This article is about that performance hit. EDIT: added a plain getter test)
Getters and setters appear frequently in the Flash API. Unless you’ve carefully inspected the Flash API documentation, you probably wouldn’t even know that such common “properties” as x and y of DisplayObject are implemented by getters and setters. Take a look at the documentation of DisplayObject and notice that x and y are listed under “Public Properties”, not “Public Methods”. Then go to the x “property” and notice that only under “Implementation” does it actually reveal the getter and setter. What kind of performance hit do you get due to Adobe’s decision to make such commonly-accessed properties getters and setters? Consider this test app:
package { import flash.display.*; import flash.geom.*; import flash.text.*; import flash.utils.*; /** * An app to test the performance of getters against public variables * @author Jackson Dunstan */ public class GetterTest extends Sprite { public function GetterTest () { var logger:TextField = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); function log(msg:*): void { logger.appendText(msg+"\n"); } const spr:Sprite = new Sprite(); const mySpr:MySprite = new MySprite(); const point:Point = new Point(); const NUM_ITS:int = 10000000; var i:int; var beforeTime:int; beforeTime = getTimer(); for (i = 0; i < NUM_ITS; ++i) { spr.x; } log("Sprite.x: " + (getTimer()-beforeTime)); beforeTime = getTimer(); for (i = 0; i < NUM_ITS; ++i) { point.x; } log("Point.x: " + (getTimer()-beforeTime)); mySpr.updateCache(); beforeTime = getTimer(); for (i = 0; i < NUM_ITS; ++i) { mySpr.cachedX; } log("MySprite.x: " + (getTimer()-beforeTime)); beforeTime = getTimer(); for (i = 0; i < NUM_ITS; ++i) { myPoint.x; } log("MyPoint.x: " + (getTimer()-beforeTime)); } } } import flash.display.*; class MySprite extends Sprite { public var cachedX:Number; public var cachedY:Number; public function updateCache(): void { this.cachedX = this.x; this.cachedY = this.y; } } class MyPoint { private var __x:Number; private var __y:Number; public function get x(): Number { return __x; } public function get y(): Number { return __y; } }
The results I get are:
Environment | Sprite | Point | MySprite | MyPoint |
---|---|---|---|---|
3.0 Ghz Intel Core 2 Duo, 4 GB RAM, Windows XP | 153 | 35 | 25 | 62 |
2.0 Ghz Intel Core 2 Duo, 2 GB RAM, Mac OS X 10.5 | 295 | 51 | 38 | 98 |
What a range! The x getter in Sprite makes is about five times slower than the public variable equivalent in Point and MySprite. This is due to the getter function call and the conversion from twisps (thanks to Keith Peters for pointing this out in the comments). Just the plain getter call in MyPoint is almost twice as slow as well. MySprite is an attempt to add the speed of public variables back into Sprite by caching the result of the getter. A real implementation would probably be much more complete, but it should give you a start if you’re interested in swapping convenience for speed.
Since DisplayObjects are so incredibly common in Flash applications and are very likely to be present in performance-critical areas, I strongly recommend revisiting those performance-critical areas if this is news to you. You may end up with a dramatic performance increase!
#1 by Keith Peters on November 6th, 2009 ·
The slowdown of sprite’s x and y is not only due to it being a getter/setter rather than a simple variable. Well yes, it has to do with that, but it’s what that setter is doing. I don’t know of all the internals, but minimally, it is rounding it to twips (1/20th of a pixel) and triggering a change in the display state of the sprite. If you want a true test of the difference between a public var and a getter/setter, make your own where all it is doing is setting the private var.
However, the whole twip thing is another reason to keep your own position values and not rely on a sprite’s position. Again, display object positions will always be rounded to the nearest .05 which can cause some serious discrepancies over time. Best to keep it as a separate number value and update the sprite as needed. Goes hand in hand with separating model and view anyway.
#2 by jackson on November 6th, 2009 ·
I have updated the article to include a plain getter test. Since I was using the getter and not the setter, I wasn’t changing the display state of the Sprite but I was still inadvertently counting the time required to convert from twips to pixels. Thanks for the correction!
#3 by Derrick Grigg on November 7th, 2009 ·
Did you run a test comparing the access of the getter/setter on the MyPoint class vs setting the __x and __y as public and accessing them directly? That would give you a true apples to apples comparison.
#4 by jackson on November 7th, 2009 ·
No, MySprite already has public properties I directly access. One can assume the speed would be the same regardless of what the class is named.
#5 by Elliot Rock on November 9th, 2009 ·
Interesting test, it is a pretty self evident when you consider a getter setter does more but both contained public vars and getter/setters aren’t great methods at tracking a large amount of iterations of classes/objects.
Both the Vector and dictionary classes are better at retrieving and storing data like you are doing.
But that is of course irrelevant to what you where trying to prove here. Good point over all, it would be good to see a raw test without sprites or using the display list.
#6 by jackson on November 9th, 2009 ·
The MyPoint class is the test of getters without using Sprite. I added it because of Keith Peters’ comment above.
I’m not sure what you mean by using Vector and Dictionary classes here. How could this be faster than accessing a public variable? Could you provide a brief code example to show the technique? I’d be very interested to see any additional technique for solving the same problem other than what I’ve already discussed in the article.
#7 by Germain LECOURTOIS on November 19th, 2009 ·
I made a test with 10 000 000 iterations and had a getX() method.
On a simple MyPoint class:
public var _x:Number;
public function getX() : Number { return _x;}
public function get x() : Number { return _x;}
Results :
MyPoint.x: 2185 ms
MyPoint.getX: 2444 ms
MyPoint._x: 638 ms
#8 by Henke37 on September 15th, 2010 ·
There is a possible reason why some of them are setters and getters: real properties can’t be overridden. The components in Flash does that as a part of their custom scaling logic.
#9 by jackson on September 15th, 2010 ·
I agree that getters and setters provide a lot of flexibility. This article was mostly written as a caution that some of these frequently-used properties are getters and setters rather than properties. With that knowledge I wanted to show the performance consequences that implies and provide a simple tactic for improving performance for those who want to blend performance with the flexibility of getters and setters.
#10 by Enrique on November 3rd, 2010 ·
Are you sure the performance is slower in real applications?
You are testing 10 million (!) of calls.
And the diference is 60[ms] for MyPoint and MySprite.
So calling 1million we are losing only 6ms, calling 100000 less than a 1[ms]… I think is not critical for performance.
Is your application accesing 10millions of getters and setters every frame?
#11 by jackson on November 3rd, 2010 ·
The point of the article is to show that getters and setters are slower than regular field access and are therefore an opportunity for optimization. To the extent that you use getters and setters rather than field access in your application, you’ll slow it down.
For example, if you have 100,000 particles in your scene and you use
Sprite
’sget x
,get y
,set x
andset y
once per particle, you’ll make 400,000 getter/setter calls. At an average slowdown of 0.0000257ms per getter call (on the 2.0 Ghz Intel Core 2 Duo in the article) compared toMySprite
, you’d incur a 10.28ms slowdown just in overhead for using getters. If you’re trying to maintain 30 FPS, you’ve spent 30% of your available CPU time just on overhead. At 60 FPS, you’ve used 60%.If you just use a few hundred getters or setters per frame in your application, you probably don’t need to worry about it.