Reputation: 7480
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
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
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
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