web oriented universal language

Flash 9 Optimizations

Posted on 2008-03-08 by Nicolas Cannasse in General

The new virtual machine than can run AS3 and haXe languages in the Flash Player 9 brings a lot of additional speed for many common operations. This is very good news for developers since you can now write more complex algorithms in Flash such as 3D or Physics engine with correct performances.

However, there are still several pitfalls since some operations are pretty slow when compared to others :

  • creating a new object is slow : if you have a lot of short living objects that are allocated frequently, it's better to create a "Allocator" class that will store a list of reusable instances.
  • calling a lot of methods is not so fast : when you have some small methods that are very often called, it's often faster to rewrite the code directly in the calling method. For example, given a Point class :
    var p = new Point(4,5);
    // the expression :
    p.add(p2.x,p2.y);
    // would be faster if written directly :
    p.x += p2.x;
    p.y += p2.y;
    
    This process of replacing a method call with the actual code is called inlining, and haXe compiler has support for an inline keyword so that you can define which methods will be automatically inlined by the compiler when called (see haXe Documentation)
  • accessing static vars and functions is slow : you can either create an instance and use instance variables and methods instead of static ones or use the haXe inline keyword.
  • reading and writing into arrays is slow : this is very bad news. The only native data structure of Flash9 is painfully slow. It's a lot faster to access a typed instance field than to access an array. One way to optimize that is to create your own linked list :
    // a cell containing instances of MyClass
    class MyCell {
        public var elt : MyClass;
        public var next : MyCell;
        public function new( elt, next ) {
            this.elt = elt;
            this.next = next;
        }
    }
    // a list managing a chained list of MyCell
    class MyList {
        public var head : MyCell;
        public function new() {
            head = null;
        }
        // add an element at the beginning of the list 
        public function add( elt : MyClass ) {
            head = new Cell(elt,head);
        }
    }
    // You can then iterate on your list this way :
    var l : MyCell = mylist.head;
    while( l != null ) {
        var e : MyClass = l.elt;
        // ... do something with e ...
        l = l.next;
    }
    

First, the good news : using a typed linked list is 2 to 3 times faster than using an Array ! (if you want to test that in AS3, make sure to type all your variables, the code here is entirely typed thanks to haXe type-inference)

Now the bad news : you will have a define a MyList and MyCell class for each different class you want to store. So you have to copy/paste the same code again and again. Using Dynamic (or * in AS3) is out of question here since that would require a cast for each reading of an element that would be slow again.

Current haXe users know that the language have typed arrays in order to ensure type-safety. You can also define your own parametrized classes, so it's pretty easy to write-once the following :

// a cell containing any instance
class Cell<T> {
    public var elt : T;
    public var next : Cell<T>;
    public function new( elt, next ) {
        this.elt = elt;
        this.next = next;
    }
}
// a list managing a chained list of cells
class List<T> {
    public var head : Cell<T>;
    public function new() {
        head = null;
    }
    // add an element at the beginning of the list 
    public function add( elt : T ) {
        head = new Cell(elt,head);
    }
}

The problem is that typed-parameters are only used for compile-time type checking. Since the Flash9 VM does not support them, they disappear in the compiled code and are then replaced by the AS3 * type, so all your accesses to the List will need an additional cast... we're back the problem.

Thinking about the problem, I was thinking then : why not let the compiler generate a specific version of the List class for each parameter it will be used ? This way it would generate a List_User and a Cell_User class that can contains User instances, and so on.... automatically at compile time !

That approach was proved quite easy to implement, so it works now in haXe. In order to let the user choose between speed and codesize, the default haXe List class has not been modified, but instead a new haxe.FastList class has been added (accessible when compiling for Flash9).

In order to create your own "generic" classes that will be specialized when used with a type parameter, you can simply have your class implements the haxe.rtti.Generic interface.

Both haxe.FastList and haxe.rtti.Generic are now on haXe CVS (you can download and compile it to give it a try) and will be part of the 1.19 Release.

Conclusion : there's a lot of painful things to do when you want to optimize your code to run fast on Flash9. But thanks to haXe inline and generics, it's a lot more easy to do while keeping your code clean and maintainable.

Comments

You are a genius. Thank you for this information.
Posted by Evan , 2008-03-08 21:33:20
Actually, why note to make FastList be the best list representation depending on the platform? It might be Array for Flash8, and standard List for Neko.. Yes, "best" is not always best, but we can aim to cover most cases, and people can always choose manually.

For this reason, however, it might be even better to leave FastList F9-specific, and make smth like haxe.OptimalList or whatever to be the general best list.
Posted by Michael Pliskin , 2008-03-08 23:18:05
"reading and writing" -- you probably mean: "Appending" and "Iterating" -- because it does not look like you are covering what arrays do best: random-accessing.
Posted by bernard , 2008-04-04 05:57:42
Hi Nicolas,

Below the result of my own bench with AS3 code (done on a dual core with Ubuntu Gutsy and the latest Flash Player 9.0.124.0 ) :

fill a linked list with 100000 elements 577 ms
iterate over a linked list of 100000 elements 46 ms
search 1000 times an element in a linked list 13608 ms
access 1000 times to an element in a linked list 4571 ms

fill an array with 100000 elements 284 ms
iterate over an array of 100000 elements 57 ms
search 1000 times an element in an array 5781 ms
access 1000 times to an element in an array 0 ms

I'm not sure that using a linked list instead of an array is really a good pratice. The only case in which the list is faster is when we iterate over it.
Posted by Abe , 2008-04-09 13:31:06
Please make sure that you are not using the Debug Player since this will negatively impact the performances of haxe.FastList, also try using haXe for the test.

My own benchs show a 2x improve of FastList vs Array iteration. I haven't tested other operations. Adding a element should be slower because it implies allocating a Cell object so yes haxe.FastList is better when mostly iterated and rarely modified.

I'm not sure what you are calling "search" and "access", but of course, accessing the nth element of the list will take O(n) on a list and O(1) for an Array, you shouldn't use such operation on lists anyway.
Posted by Nicolas , 2008-04-15 10:28:08
Have you tried testing the linked list speed against a Dictionary class? I've done tests in the past that show Dictionary running faster than Array for modifying and iterating through a set of values
Posted by Rob , 2008-04-18 12:42:33
fill an array with 100000 elements 284 ms
iterate over an array of 100000 elements 57 ms
search 1000 times an element in an array 5781 ms
access 1000 times to an element in an array 0 ms
Posted by kuri , 2008-04-29 11:13:54

Post a comment

Name:
Email:
Url:
Security: Please enter 1829 here.
remember me
Comment:
 
 
Haxe Powered Rss flux Valid XHTML 1.0 Valid CSS