Redundant Code
Why do I see so many AS3 programmers writing so much redundant code? Are you one of them? Today’s tips may save you a lot of typing. It may even save you a lot of SWF size.
There’s a certain unease programmers get about some language features. Maybe you come from a language like C or C++ where variables are not automatically given default values and it worries you when you see code like this:
class Person { public var verboseLogging:Boolean; private var name:String; public function set name(newName:String): void { if (verboseLogging) { trace("Changing name from " + name + " to " + newName); } name = newName; } }
The cry will go out: “what if the user never sets verboseLogging
!?” Thankfully, we know that is is redundant to initialize verboseLogging
to false
because the Flash Player will do it for us. In C/C++, accessing verboseLogging
would result in undefined behavior since that memory could have been set to anything before. In AS3, we’re guaranteed that it’ll be false
.
“I’ve known that since you wrote about it in 2009″, you may rightly say. Or maybe you knew about it even before that. Well, feel free to shake your head every time you see someone’s AS3 code redundantly initializing a variable. But before you get too presumptuous, let’s talk about a couple more areas where you may be writing redundant code. First, let’s consider some if
statements. How many of these do you write?
function verbose(b:Boolean, o:Object, s:String, i:int, n:Number): void { if (b == true) {} if (b == false) {} // UPDATE: this should really be a Point or some other Object derivative if (o != null) {} if (o == null) {} if (s != null && s.length > 0) {} if (s == null || s.length == 0) {} if (i != 0) {} if (i == 0) {} if (n != 0 && !isNaN(n)) {} if (n == 0 || isNaN(n)) {} }
All of these can be written with less typing and less bytecode. Consider these alternatives:
function concise(b:Boolean, o:Object, s:String, i:int, n:Number): void { if (b) {} if (!b) {} if (o) {} if (!o) {} if (s) {} if (!s) {} if (i) {} if (!i) {} if (n) {} if (!n) {} }
All of these (even strings) are functionally equivalent and take advantage of AS3 bytecode’s iftrue
and iffalse
instructions’ support for non-Boolean
types rather than pushing an extra argument (true
, false
, null
, etc.) and comparing with an ifeq
or ifne
instruction. Consider the bytecode for the if (o != null)
test: (which jumps over a block of code when o
is null
)
// verbose 14 getlocal2 15 pushnull 16 ifeq L3 // concise 12 getlocal2 13 iffalse L3
In addition to less typing, there’s less bytecode to bloat up the SWF. One instruction isn’t much bloat on its own, but over thousands or tens of thousands of lines of code it really adds up. Is there a corresponding performance loss to add up too? Well, let’s look at a quick test:
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class VerboseConcisePerformance extends Sprite { private var __logger:TextField = new TextField(); private function log(msg:*): void { __logger.appendText(msg + "\n"); } public function VerboseConcisePerformance() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); const REPS:int = 100000000; var beforeTime:int; var afterTime:int; var i:int; var nullObj:VerboseConcisePerformance; var notNullObj:VerboseConcisePerformance = this; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { if (nullObj != null) {} } afterTime = getTimer(); log("verbose null: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { if (notNullObj != null) {} } afterTime = getTimer(); log("verbose not null: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { if (nullObj) {} } afterTime = getTimer(); log("concise null: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { if (notNullObj) {} } afterTime = getTimer(); log("concise not null: " + (afterTime-beforeTime)); } } }
Here are the results I get with the Flash Player 10.1.102.64 release plugin on a 2.4 Ghz Intel Core i5 running Mac OS X 10.6:
Verbose | Concise | ||
---|---|---|---|
Null | Non-Null | Null | Non-Null |
215 | 213 | 210 | 210 |
When you’re running a loop a hundred million times and finding performance differences like 2 milliseconds (depending on test run), you haven’t really found a performance difference. I’d speculate that the JIT is optimizing one version into the other.
One last little application of the above: you don’t need to initialize your loop iterator! Consider a standard for
loop:
for (var i:int = 0; i < len; ++i) { }
I don’t know about you, but I can type that loop in my sleep. OK, maybe you use i++
instead. Coming from C/C++, it’s hard for me to remember that I don’t need to initialize i
. This loop is equivalent:
for (var i:int; i < len; ++i) { }
But there is a gotcha here: variables are “hoisted” to the function level in AS3, so you can only get away with not initializing i
on the first loop. Subsequent loops need their iterators initialized because they will re-use i
. For example, this will fail miserably:
for (var i:int; i < len; ++i) { } for (var i:int; i < len; ++i) { }
The i
iterator will end the first loop with a value of len
and not be set back to 0 for the second loop. You’l get a compiler warning from MXMLC telling you that you’re re-using i
, so at least you’ll be warned about the problem at compile time.
In conclusion:
- Don’t redundantly initialize your variables
- Don’t do needless checks in your
if
statements (unless you really like the aesthetics)
I hope you’ve read something in today’s article that will save you some typing, some SWF size, or both.
#1 by pleclech on February 7th, 2011 ·
Beware of shortcut, for example if (o) is not the same as if (o!=null) :
#2 by jackson on February 7th, 2011 ·
Excellent point!
Perhaps I shouldn’t have used
Object
in the article. I originally usedPoint
, but it’s really meant to be anyObject
derivative.EDIT: I’ve updated the article with a note your point.
#3 by ramindeja on February 7th, 2011 ·
Hi,
I can agree initializing variables upon declaration is not necessary, but writing a clear and auto-documented condition should not be ignored just because Adobe does not provide better code optimizers. You’re point is taken for a heavy CPU intense code, but on normal cases, I’d like to distinguish between a Boolean variable and a reference in a condition[if (ref != null), rather than if (!ref)].
Two years ago, on Flash On The Beach you could really see my above point. Grant Skinner was showing techniques to enhance performance. Most of the hints and advice was basically promoting writing bad code. On the other hand, Joa Ebert’s optimizer was letting you write a proper, clean code and would let the optimizer do the dirty work.
He is right, Adobe can easily remedy the situation by providing code optimizers that one person can write, they have an army of language and compiler specialist.
I like Flash/ActionScript and I hope that very soon this technology becomes as mature and efficient as Java, C++ (even HaXe).
#4 by jackson on February 7th, 2011 ·
Code readability is definitely an important factor and should not be discarded lightly. One must always balance performance with maintainability while taking into account the particular situation, both technically and from a human perspective. That said, in the case of your example I prefer to use variable naming to distinguish between a reference and a
Boolean
. For example:Also, I totally agree that the compiler should negate any of the performance/size differences I wrote about in the article, but even after 4+ years of AS3 it continues to leave in this redundant code. To my knowledge, the next version of the Flex SDK (Hero, still in beta) is not slated to add any optimizations like this. So while you’re right that Adobe should (and even could) solve the performance/size problems much more cleanly than the above workarounds, it looks like they won’t do so in time for any AS3 projects shipping in the near term.
Also, the above techniques really do save you a ton of typing. Typing takes time and wears on your body. It may seem silly, but when you type for 8+ hours a day it really does become a factor worth considering. To me, if I can avoid some typing, I’ve made myself more productive lowered the physical stress.
#5 by giorgio on February 7th, 2011 ·
What about reusing the ” i ” variable?
#6 by jackson on February 7th, 2011 ·
You certainly can! There are plenty of good re-uses of loop iterators, especially considering that the compiler will not re-use them for you. For example:
The above will actually generate bytecode that uses two local
int
variables rather than noticing that just one is needed and combining them to something like your version.#7 by as3isolib on February 7th, 2011 ·
Curious Jackson.
Have you seen any difference in loops such as:
var i:
for ( i; i < len; i++)
vs.
var i;
for ( ; i < len; i++)
where basically the first initializing statement in the for loop is left blank? I have seen that in both code examples on your blog as well as elsewhere. I can't imagine the performance boost would be anything substantial, but out of curiosity I'd like to know.
#8 by jackson on February 7th, 2011 ·
Hmm, I didn’t know you could leave the first statement of the
for
loop out…Anyhow, here’s the test:
And here’s the bytecode (MXMLC 4.1):
No difference in bytecode, so there’s no difference in performance.
#9 by Sebastien on February 7th, 2011 ·
Actually, all statements are optional. This is perfectly legal : for (;;){}
The first statement is always executed before the loop begins, the second is always run before the body of the loop and the last one is always run after the body of the loop, but it can all be managed inside the body of the loop if you decide so (for some obscure reason).
Well, since you NEED the two “;”, these are not omitted instructions, but empty instructions…
#10 by skyboy on February 7th, 2011 ·
Oh wow. That’s unexpected, the compiler actually optimized something; by removing the var access.
#11 by skyboy on February 7th, 2011 ·
One note about not initializing iterators though,
Because j is not set back to 0 at the start of the loop. This little gotcha has gotten a few people I’ve helped out.
#12 by DieTapete on February 9th, 2011 ·
Well, I wouldn’t really call this redundancy..
I just tried the for(var i:int; thing and it led to an ugly bug because I had already used the variable earlier.
So I would say writing the =0 afterwards is not redundancy but best practice.
#13 by jackson on February 9th, 2011 ·
This is exactly the “gotcha” that I pointed out and explained at the end of the article. The
=0
is redundant, but only the first time you use the variable. It’s fine if your best practice is to use=0
(or the default value of any other type), but you should be aware that your best practice does include redundancy and therefore requires extra typing and bigger SWFs for the sake of uniformity. There’s no right or wrong decision there as it really depends on the particular project, programmers, etc.#14 by Rasheed Abdul-Aziz on February 19th, 2011 ·
I wouldn’t always use your optimisations. I am not as picky about speed as legibility, and as I’ve argued on twitter a lot, knowing what the short hand [in]equality tests boil down to is not something I want to expect of my team.
However! When optimization is important, it’d be nice to have a cheat sheet for these, then i realized, better still, a set of flex pmd rules, on say github. If you ever find yourself with a little extra energy :)
Thank you for the post
#15 by Sandro Manke on March 11th, 2011 ·
Hey Jackson,
i havent read all comments now, but i guess i am not the only one who favors code readability over “redundancy”. what you call redundancy here is information which might be very crucial to easily understand code. in my opinion code should be readable like a good easy SMALL book, so that you easily understand constraints without needing to know everything everywhere.
example of yours:
if (b) {}
if (!b) {}
this is actually wrong for 2 reasons.
#1 – what the f*** is b? naming please
#2 – on what tests it? null? true? number?
as for reason #1. in reading code unexplainable variables lead to reuse of the same variables throughout the code for different meanings, which results in messy, overwhelming and overly hard to optimize code.
considering reason 2, you need to know for a variable, which type it is on all the funciton length and you cant just simply scan it without considering in real life a massive amount of information.
in my opinion it might be redundant for the compiler. yes. it is.
but we are not machines.
we are humans.
humans code.
machines can optimize code very good. (as for jit, global optimization c++ etc, there are a lot of great techniques even including logging of user behaviour and the optimization on exactly that one.)
but there is a second way of optimization machines can not do.
creativity.
i rest my case here for the comment, but if you want to discuss that, feel free :)
#16 by jackson on March 11th, 2011 ·
#1 – “b” is whatever you decide to name your variable. Hopefully you’ll choose something appropriate to your function or class. For the purposes of this article, I just chose “b” because the variable was a
Boolean
. I suppose I could have chosen an example variable name like “mouseDown”.#2 – On the plus side, including the
== null
or the like does make the code more explicit. On the minus side, the SWF will become larger due to the extra bytecode generated by MXMLC and you will have to type more characters to write the same effective code. So, there is a judgement call here. My own preference is to reduce typing (which adds up when writing code is your full time job) and SWF size by simply requiring that those reading my code understand the well-documented equality operator.PS: Please don’t swear on my site. I’ve edited your comment to remove the swearing.
#17 by Sandro Manke on March 12th, 2011 ·
First, sorry for the swearing.
But i have one problem with this approach and documenting it. It seems, that people actually hear you and do the same. For readability and maintainability reasons, your code is a freaking nightmare, cause it needs more semantics to understand it. I need to know in the moment of that check, which type this variable is.
Actually i know since the beginning of as3 (in as2 this was actually a bit more f****d, like in javascript) about these possibilities and was tempted to use them, but thinking got me away from ever doing this.
I am not so sure – for what do you write your code? Modem 1200 Baud?
Where do those simple things come in to be a real problem, other than in banners, which are indeed a very special case in optimizing. I have a background myself in the demoscene and regarding 4kb intros this approach might be a good idea. But rest assured, that for 64kb intros it is not anymore.
In my opinion there is no judgement call. Writing understandable code, that is easy for others to understand in the fastest possible manner, to be able to #1 use it #2 maybe fix it #3 extend it and what other things you can think of is the primary goal of good programming.
little anectode:
one of my partners in our company just wrote down a completely unoptimized raytracer in cuda. this one is actually by far the fastest raytracer in the world right now. he compiled this code, cause it was simple c, and therefore easy to port for current processors. there was a possibility to match it up against a heavily optimized raytracer (probably the best one you can get, but not official, expensive) – optimized in assembler and what else.
guess who was faster?
again
code optimization does not lead to fast, efficient programs. it did 30 years ago. todays compilers are better than you.
the key to efficient programming is understandable extendable code, which is not bloated (like most of the frameworks) and therefore easy to optimize on a logical level. this means mathematically. finding out which paths are slow, or called too often and optimize the structure and data flow, till everything adds up to be great.
this works for projects you probably have no idea of as an as3 programmer
but rest assured – this also works very good in as3.
my main problem is, that i come to companies on consulting and find code like this and could instantly start screaming, what disease has smitten the poor programmers, to do this :(
in the end you say, you spend the most time writing code.
i dont.
i spend about 5% of my time really coding, but can outperform other programmers in speed easily.
where does it come from?
thinking.
that is where you can optimize, and that is why those types of less easy understandable code are a problem. it IS understandable, it just takes the brain longer and is more complicated, and therefore your brain is less able to get the whole picture.
when i see == null, i know instantly “this can not be an int” – quite nice to know fact
when i see == true – oh boolean!
i dont even have to see the definition and scroll around, or memorize any variables. it is just not needed anymore.
what you say about redundancy is true in one regard and i gave you that already: it is redundant for the machine. i am completely with you on that.
but i am human :)
so are a lot of others i guess
#18 by Sandro Manke on March 12th, 2011 ·
hups, i guess you need to edit again :(
its a topic which is quite near to me and therefore there is a lot of emotions. sorry for that.
#19 by jackson on March 12th, 2011 ·
I edited your comment to remove the swearing.
As long as there are both pros and cons to the shorthand (
if (isAlive)
), then there will always be a judgement call. You argue passionately against the shorthand because you prefer the readability of the longhand (if (isAlive == true)
) and that is completely fine.You’re correct that, for most AS3 applications, the extra SWF size is irrelevant. However, this is a site for all kinds of AS3 applications, including banner ads and the 4KB demo scene. Some of my readers, rest assured, are very interested to know such details of the language they use on a daily basis. The unfortunate fact is that MXMLC currently does not generate the same bytecode for the shorthand and therefore our “machines” are currently failing to do the optimizations that we shouldn’t have to. For those 4KB and banner ad coders out there who want to work in plain AS3 with MXMLC and not resort to hand-written assembly (far uglier), switch languages entirely to haXe (which may or may not generate better bytecode in this case), or some other approach, this is useful optimization advice right now.
As for typing code, it’s great that you’ve found a way to be productive whilst only typing 5% of the time. However, I find this to be extremely rare. Most professional programmers I know type for at least five hours a day. For these readers, the shorthand approach is a way to save them a lot of typing in their daily lives and therefore reduce the stress on their bodies (it adds up) and save them time.
In the end, it’s a judgement call. There’s no reason we all must make the same call. You’re free to keep writing
== true
and I’m free to leave it out.#20 by skyboy on March 13th, 2011 ·
In code that is run many times, some very simple optimizations can lead to massive performance gains. In a pet project I worked on today, small optimizations like this saved, in total, 50 ms. Which may not seem like much, but these are things run every 30 ms.
I’ve since done more heavy optimizations on the basic structure and display of its output that brought total execution speed down to virtually nil (0-1 ms average). However, this will also be going back up soon as I reimplement a procedure to double (or more) the scale and add detail that doesn’t exist in the smaller version. The up-scaling was prohibitively expensive (80% total execution time), but under the new model this should be significantly lower.
With some luck, I can get it below being an O(n^6) function as well.