Jesse Emond
Jesse Emond

Reputation: 7480

AS3 - Converting an expression to a string

Is there a way to convert an expression in AS3 to a string?

I'm making a very small scale profiler for my Flash AS3 files to find the bottlenecks and here's the current syntax (which is tedious):

PerformanceProfiler.start("updatePlayer");
updatePlayer(map, enemies);
PerformanceProfiler.stop();

I eventually call PerformanceProfiler.show() to get the results from trace calls later on.

Internally, the PerformanceProfiler keeps a dictionary linked by the names of the functions, such as "updatePlayer", to a list of the total milliseconds passed on it. The show function only lists the function names along with the total time spent on each.

For my needs, this does the trick.

What I'd like, however, is to avoid the whole "updatePlayer" thing, because it's repetitive. In fact, I'd like to get rid of the start and stop calls completely.

My idea was to call it with some sort of anonymous function, something along the lines of (sorry, I'm not sure about the exact syntax of that feature):

PerformanceProfiler.profile( { updatePlayer(map, enemies); } );

And would only display the results this way:

{ updatePlayer(map, enemies); } - 1202

{ updateEnemies(map, player); } - 5126

...

So, is there a way to get the content of an anonymous method as a string? This would be fantastic. Though, if you have a simpler solution to my problem I would truly appreciate, since we're getting frame rate drops that we just can't spot right now, and don't want to waste time optimizing what could simply be taking 1% of the processing time.

In summary, what I want is:

{ updatePlayer(map, enemies); } converted to "{ updatePlayer(map, enemies); }" in AS3.

Thank you very much for your precious time.

Upvotes: 0

Views: 211

Answers (3)

divillysausages
divillysausages

Reputation: 8033

Another answer because why the hell not :)

package  
{
    import flash.display.Sprite;
    import flash.system.System;
    import flash.utils.describeType;
    import flash.utils.Dictionary;
    import flash.utils.getTimer;

    public class TestDescribeType extends Sprite
    {
        private var m_cache:Dictionary      = null; // a cache that says if we've used this object before
        private var m_functions:Dictionary  = null; // the functions for this object

        public function TestDescribeType() 
        {
            this.m_cache        = new Dictionary;
            this.m_functions    = new Dictionary;

            this.profile( this, this.one );
            this.profile( this, this.two, 4.5 );
            this.profile( this, this.three, 4.5, "hello" );
            this.profile( this, this.four, 4.5, "hello" );
        }

        // profiles how long it takes a function to complete
        public function profile( obj:*, func:Function, ...params ):void
        {
            // if we don't have this object in our cache, parse it
            if ( !( obj in this.m_cache ) )
                this._parse( obj );

            // get our functions dictionary and get our function name
            var dict:Dictionary = this.m_functions[obj];
            var funcName:String = ( dict != null && ( func in dict ) ) ? dict[func] : "Unknown func";

            // start our timer and call our function
            var startTime:int = getTimer();
            func.apply( null, params );
            trace( "  " + funcName + " took " + ( getTimer() - startTime ) + "ms to finish" );
        }

        // some test functions
        public function one():void { trace( "one()" ) }
        public function two( one:Number ):void { trace( "two(" + one + ")" ) }
        public function three( one:Number, two:String ):void { trace( "three(" + one + ", " + two + ")" ) }
        public function four( one:Number, two:String, three:int = 0 ):void { trace( "four(" + one + ", " + two + ", " + three + ")" ) }

        // parses a class/object and removes all the functions from it
        private function _parse( obj:* ):void
        {
            // get our xml
            var x:XML           = describeType( obj );
            this.m_cache[obj]   = true; // we don't need to store the xml, just that we've parsed it

            // find our functions
            var funcs:Dictionary = new Dictionary;
            for each( var mx:XML in x.method )
            {
                var name:String     = mx.@name;
                var func:Function   = obj[name] as Function;

                // store the function with the name etc in our dictionary
                funcs[func] = name;
            }

            // store our functions
            this.m_functions[obj] = funcs;

            // kill our xml object immediately - helps memory
            System.disposeXML( x );
        }

    }

}

Run the code to get an idea of what it does. Basically, we do a describeType() on the object the first time we encounter it, and take out all of it's functions. Then, the profile function takes what we want - we check out cache to get the right name, then call it.

Has the advantages of being quicker than throwing an error (the initial describeType()) on an object is kinda slow, but it's a once-off, and you could probably add a pre-treat step if you really want to.

You'd need to rework it so that the describeType() only works on the class, not on the object (as if you have 10 Sprites, it'll do 10 describeType()s). If you do that, then the m_functions Dictionary will need to be set up per object, as otherwise the (func in dict) check will fail for all instances of an object except the first one, if you see what I mean.

Anyway, you get the idea :D

Upvotes: 0

divillysausages
divillysausages

Reputation: 8033

While it doesn't exactly answer your question, check out the flash.sampler package: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/sampler/package-detail.html

Using this, you don't need calls to PerformanceProfiler at all, you just need to parse through the samples returned.

If you want to get a quick idea of it, check out the miner: http://www.sociodox.com/theminer/index.html

it's pretty easy to add to your project, and has all this samples/profiling already

Upvotes: 1

turbosqel
turbosqel

Reputation: 1542

I dont think that there is simple way to do that and know which functions was called without profiler.

But You can create function that will read Namespace.Class.Function names and check parameters using 'Error' and 'arguments' :

public function someFunction (param1:Object , param2:Object ):void {
   Profile(arguments);
   // function body ...
}

////////

public function Profile (args:Object):void {
    trace(new Error().getStackTrace()); // from this string You will read func name
    for each(var o:* in args) {
    trace("param:",o); // here are parameters
    }
}

Upvotes: 3

Related Questions