Thursday, November 08, 2007

AVM2 vs JVM and ActionScript3 Performance

AVM2 is Adobes virtual machine in Flash Player 9, that executes ActionScript3 (AS3) bytecode. If you are accustomed to writing code for the great JVM and are starting to write Flex or AIR applications, there are things you should be aware of.

In this blog, I present information on how the AVM2 works, and also show some tips on how to make your code perform better on AVM2.

Most of the information in this blog should be credited to Gary Grossman from Adobe. I found a (great) presentation with his name on it, entitled "ActionScript 3.0 and AVM2: Performance Tuning". I was mostly interested in the AVM2 part, and luckily, the 74 pages of the presentation had quite a bit of information on that subject.

Types and Type Information
Prior to AS3, all type information was stripped out of the code when compiled. At runtime, everything was just dynamically typed atoms. Starting with AS3, when can get the type information all the way down to the runtime.

Another point in the introduction of the native int and uint types, which can make code perform better.

Here is how to take advantage of this great new stuff:

First of all, start working with typed variable. That is, do this:
var document : XML = ....;
and not this:
var document = ...;
var document : Object = ...;
var document : * = ...;
Using explicitly typed variables can improve performance and reduce memory consumption. As a little side note, going from untyped, or dynamically typed to the explicit type information, enables the compiler to catch more errors early on in the development cycle, but that is another story :-)

Another advice is to use the native int and uint types where possible, instead of using the Number object type. But beware, as compliance with ECMAScript requires the VM to promote basic int types to Number sometimes. For example in this code:
var i:int=1;
methodCall(i + 42); // will promote to Number
If you know, that it will be within an int type, you can help the compiler by introducing a coercion like this: methodCall(int(i+42)).

Also, array index with int and uint types have fast paths when compared to Number, so consider coercion of array indexing too.

Common Subexpression Elimination (CSE)
When writing Java code, I am accustomed to not thinking that much about common subexpressions in loops etc. I let the compiler and the VM handle all that optimization. It is pretty good at it, and me trying to help it might very well destroy its possibilities of doing it well.

AVM2 can do CSE too, but not as good. For instance, in this simple code:
for (var i:int=0; i < arr.length; i++) {
callMethod(arr[i]);
}
the arr.length expression is evaluated on each iteration of the loop. This is due to the fact, that it might have sideeffects or be dynamically overridden. So, do this:
var len:int=arr.length;
for (var i:int=0; i<len;i++) {
callMethod(arr[i]);
}

Method Closures
In AS3, we got method closures, which also mean we can create variables that are functions, and pass them around, while the function still retains the environment, it was created within ("this" is still what "this" was, when defined).

If you write code like this, with inner, anonymous function closures:
function foo() : void {
var handler : Function = function(event:Event) : void {
// blah blah
}

addEventListener("someEvent", handler);
}
The definition of the handler variable, will force a creation of an "Activation" object, that is to hold the environment state, at time of creation. You can get better performance and memory utilization by doing it this way:
function handler(event:Event) : void {
// blah blah
}

function foo() : void {
addEventListener("someEvent", handler);
}
as this will utilize the method closure functionality of the AVM2, but without an activation object.

JIT Compilation and Constructors
Now that flash player is a stackbased, byte-code oriented virtual machine, it is natural for it to have JIT compilation. And so it has. I think it is far from the JVM hotspot and compiler, but we must also recognize, that flash player clearly has another target. But the objectives of the JIT compiler are good, I think. They are:
  • Fast compile times
  • Limited passes
  • Cautious with memory
The fact that AVM2 does JIT compilation might also be why there is no 64bit version of the player as of yet. Doing JIT requires extra work for each CPU to target. AVM2 does have a MIR format (Macromedia Intermediate Representation), which is uses on its way from byte-code to JIT'ed machine code. The responsibility of the MIR is to abstract commonalities between CPUs, but it must still be extra work again and again for each architecture, when doing JIT.

There is one thing to take notice of with JIT in AVM2. Constructors are not JIT'ed, so if you have performance intensive code in a class, take it out of the constructor.

Garbage Collection
The memory management and garbage collector is to be found in the separate MMgc project. This is a Deferred Reference Counting (DRC) mechanism combined with an incremental, conservative mark/sweep collector. Of course, the garbage collector implementation has been tuned for best client performance, with small (30ms) time slices.

Overall, I find the new flashplayer architecture much nicer, than I have previously thought of it. I guess it has also come a long way, suddenly growing up to serve us real, complex applications on the web. This all actually gives me more peace in mind, with respect to the flex applications we are building. Large applications on the flex platform will have a hard time succeeding, if their executing runtime (the AVM2) is not up for the task of running real applications.

I am confident that it is!

2 comments:

opinali said...

What a cheap Java knockoff! I didn't previously know anything about Flash and Adobe's VMs, but reading the whole PDF, I could just delete a couple pages (like those about traits and prototypes), translate some syntax, and have a perfectly good intro to the JVM architecture. Yeah, down to the existence of separate client and server JITs and bytecode verification. I wonder why Adobe just didn't license the JVM - or even use it for free, it's open source now - they could just ditch the whole Java class libraries (it's the only thing that makes the JRE big and slow-booting, e.g. Swing), add a couple additional bytecodes to better support compilation of ActionScript, and profit from first class interpreter, GC, JIT and other technologies.

Per Olesen said...

Hi Opinali,

Java Knockoff. Maybe, maybe not. I come from the Java platform myself, and use it day in and out. And yes, there are quite a few similarities.

But I see that as quite natural. I don't see Adobe as having copied JVM technology, but more having accepted the VM-based byte-code execution model as being a good thing, therefore implementing a stack based on this.

Strictly speaking, the JVM has not innovated this technology stack at all either. It "just" proved it could work big time, and took it main stream.

Others have followed. Microsoft with .net, which is the same stack, basically. The new Ruby 1.9 will be a bytecode-based, stack-VM as far as I know. Parrot, .. maybe :-)

Why Adobe didn't just use the OpenJDK. Well, I think I can answer that. Adobe has NOT been open sourcing their player/runtime. And it doesn't look like they will (and that is sad, yes). The OpenJDK is released under a GPL license, which would require Adobe to release their build/runtime based on it as GPL too.