1
votes

It's been shown (e.g. references in AS3: cast or "as") that it's 3-4x faster to use the "as" keyword versus parenthetic casting in AS3. This is because (cast) could better be described as interpolation, actually generating a new object rather than truly casting the old one. (cast) throws a type error if it fails where the "as" operator returns null. Alright. Three questions here:

(1) What's happening when you pass a Number to a function that expects (int) -- or when you pass a Sprite to a function that expects DisplayObject? i.e. if the function expects a parent class. Is a new, locally scoped object being generated if the expected class is an ascendant of the object you pass as an argument? Is it significantly faster to cast your arguments before calling the function, using "as"?

(2) What's happening in a for each (var i:int in someNumberVector) or a for-each loop treating each Sprite as a DisplayObject, for example? Is every single one re-cast via the slow (not "as" but error-prone) method at each step in the loop?

(3) Does having a function or variable assignment expect an Interface-implementing class (or an Interface itself, e.g. IEventDispatcher) make any difference vs. having it expect a parent class, as far as which method (clone or cast) is used to "cast" the original variable?

1
(1) Number -> int: slow, has to convert. Sprite -> DisplayObject: fast, no work to do. (2) Iterating int vector shouldn't require any extra work (depending on the internal implementation of vector), unless the vector is not declared as a vector of ints, but as a vector of something else, in which cast each iteration requires a conversion. Sprite -> DisplayObject is again instant, because there's nothing to do. (3) Nope. - Cameron
Okay, just to clarify/expand on this: If I have a dynamically loaded instance -- let's call it var bit:* -- which I know extends Bitmap (for example, the .content of a Loader) and I want to access a Bitmap method on it like getting its bitmapData, I know it's much much faster for me to say (bit as Bitmap).bitmapData than it is to say Bitmap(bit).bitmapData. So my question pertains to what happens if I send this variable as an argument into a function that expects a Bitmap. Which type of casting takes place then? And why would it be compiled differently if we knew 100% the arg extended Bitmap? - joshstrike
* is not a type; it expands to the type of whatever you assign to that variable at compile time (type inference). What happens depends on the type of the variable. This is because if the type extends Bitmap, the compiler knows at compile time that whatever the variable references at runtime will be a valid Bitmap (or null), regardless of whether the actual object is of a subclass or not. If the type is, say, Object, then the object cannot just be blindly used as a Bitmap, since at runtime the variable could reference any type of object, not just a Bitmap. ... - Cameron
... So the compiler has to generate code to do type checking in addition to the cast (the cast itself should be free if the type is indeed Bitmap or a derivative). I suggest decompiling your SWF and looking at the generated bytecode if you want to know what's really going on (though that won't tell you how fast it's happening, which you should benchmark in your specific circumstances as part of your larger application instead of in a micro-benchmark). I hope all this makes sense :-) - Cameron
As a general rule of thumb, I would suggest using the actual types (or base types for class objects) whenever possible (i.e. strong typing, where it can be checked at compile time instead of runtime -- this results in fewer bugs and faster code), instead of types like Object. Treat subclass-to-base casting as free, and try to avoid all other casts where possible (obviously sometimes it makes sense to cast, or is unavoidable, but there's no reason to be casting everything everywhere when there's perfectly good strong typing available). - Cameron

1 Answers

1
votes

When you pass an object of a subtype (or iterate a list of subtypes, etc.), no casting or conversion is required -- the object is that subtype, and can be passed directly. The reason for this is that the memory layout of objects with inheritance is usually implemented as the base object's data, then the next inheriting class's data, etc. plus a vtable pointer to allow method overriding/interface implementation:

// class Sub : Base
obj -> +----------+
       |vtable ptr| -.      vtable
       |----------|   `-> +---------+
       |Base data |       | Method1 |
       |----------|       +---------+
       |Sub data  |       | Method2 |
       +----------+       +---------+

When passing a Sub object, the underlying pointer points to the start of the object in memory -- which is exactly the same as the Base object's start in memory, so there is nothing to do when converting between Sub to Base internally; the only difference is how the variable is used (type checking semantics). The actual object value, and references to it, need absolutely no conversion (in a sane implementation, anyway).

Adding a cast would only slow things down (unless the compiler is smart enough to remove the cast, which it should be, but I don't have much confidence in the optimization capabilities of the AS3 compiler).

However, casting Numbers to ints and vice versa is completely different, as that requires converting the internal representation of the values -- which is slow.

Note the difference between references to objects (where the variable's actual value is just a handle to that object), and value types (where the variable's actual value is the value itself -- no indirection). For example, an int variable holds the actual integer variable, whereas a variable of type Object merely holds a reference to some object. This means that if you use variables of type Object (or untyped variables which are of Object type by default), and try to stick an integer in them, this will cause what's called a "boxing" operation, which takes the value and sticks it into a temporary object so that the variable can keep a reference to it. Manipulating it like an integer (or explicitly casting it), causes that boxed value to be unboxed, which obviously is not instant.

So you can see, casting objects to their base is not slow, because there's no work to do. Casting a base to a derived type is almost as fast (because nothing has to be done to the internal pointer there either), except that a check has to be done to ensure that the object really is of the derived type (resulting in an exception or null depending on what type of cast was done). Casting value types to object types and vice versa is not very fast, because work has to be done to box/unbox the value in addition to a runtime type check. Finally, casting one basic type to another is particularly slow, because the internal representations are completely different, and it takes correspondingly more effort to do the conversion.