Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flash / Actionscript CPU profiler

Have you found such a tool and used it successfully?

like image 773
joeysim Avatar asked Dec 10 '08 22:12

joeysim


3 Answers

I was also looking for a profiler for AS, but I wanted an freeware/open source solution that works with FlashDevelop and Flex SDK. I found none. So I wrote a simple python script and an even simpler AS class. The script essentially takes any AS file and adds profiling code (i.e. calls to measure the total runtime of that function with an accuracy of 1 ms - the resolution of the flash.utils.getTimer() call) to each function definition. The script sometimes makes mistakes, but these are usually easy to fix by hand. Then you need to add one more line manually: dump the profiling statistics somewhere at some point. This method is obviously far from accurate, but it nonetheless gives you good feel of bottlenecks in your code. I used it for a 100k file with success.

Here is the AS class:

package  {
    public class Profiler {
        private static var instance:Profiler;

        public static function get profiler():Profiler {
            if (!Profiler.instance) Profiler.instance = new Profiler;
            return Profiler.instance;
        }

        private var data:Object = {};

        public function profile(fn:String, dur:int):void {
            if (!data.hasOwnProperty(fn)) data[fn] = new Number(0);
            data[fn] += dur / 1000.0;
        }

        public function clear():void {
            data = { };
        }

        public function get stats():String {
            var st:String = "";
            for (var fn:String in data) {
                st += fn + ":\t" + data[fn] + "\n";
            }
            return st;
        }
    }
}

And here is the python script that does the trick:

import sre, sys

rePOI = sre.compile(r'''\bclass\b|\bfunction\b|\breturn\b|["'/{}]''')
reFun = sre.compile(r'\bfunction\b\s*((?:[gs]et\s+)?\w*)\s*\(')
reCls = sre.compile(r'class\s+(\w+)[\s{]')
reStr = sre.compile(r'''(["'/]).*?(?<!\\)\1''')

def addProfilingCalls(body):
    stack = []
    pos = 0
    depth = 0
    retvar = 0
    klass = ""
    match = rePOI.search(body, pos)
    while match:
        poi = match.group(0)
        pos = match.start(0)
        endpos = match.end(0)

        if poi in '''"'/''':
            strm = reStr.match(body, pos)
            if strm and (poi != '/' or sre.search('[=(,]\s*$', body[:pos])):
                endpos = strm.end(0)

        elif poi == 'class':
            klass = reCls.match(body, pos).group(1)
            sys.stderr.write('class ' + klass + '\n')

        elif poi == 'function':
            fname = reFun.match(body, pos)
            if fname.group(1):
                fname = klass + '.' + fname.group(1)
            else:
                lastf = stack[-1]
                lastf['anon'] += 1
                fname = lastf['name'] + '.anon' + str(lastf['anon'])
            sys.stderr.write('function ' + fname + '\n')
            stack.append({'name':fname, 'depth':depth, 'anon':0})

            brace = body.find('{', pos) + 1
            line = "\nvar __start__:int = flash.utils.getTimer();"
            body = body[:brace] + line + body[brace:]
            depth += 1
            endpos = brace + len(line)

        elif poi == '{':
            depth += 1

        elif poi == 'return':
            lastf = stack[-1]
            semicolon = body.find(';', pos) + 1
            if sre.match('return\s*;', body[pos:]):
                line = "{ Profiler.profiler.profile('" + lastf['name'] + \
                       "', flash.utils.getTimer() - __start__); return; }"
            else:
                retvar += 1
                line = "{ var __ret" + str(retvar) + "__:* =" + body[pos+6:semicolon] + \
                       "\nProfiler.profiler.profile('" + lastf['name'] + \
                       "', flash.utils.getTimer() - __start__); return __ret" + str(retvar) + "__; }"
            body = body[:pos] + line + body[semicolon:]
            endpos = pos + len(line)

        elif poi == '}':
            depth -= 1
            if len(stack) > 0 and stack[-1]['depth'] == depth:
                lastf = stack.pop()
                line = "Profiler.profiler.profile('" + lastf['name'] + \
                    "', flash.utils.getTimer() - __start__);\n"
                body = body[:pos] + line + body[pos:]
                endpos += len(line)

        pos = endpos
        match = rePOI.search(body, pos)
    return body

def main():
    if len(sys.argv) >= 2: inf = open(sys.argv[1], 'rU')
    else: inf = sys.stdin
    if len(sys.argv) >= 3: outf = open(sys.argv[2], 'wU')
    else: outf = sys.stdout
    outf.write(addProfilingCalls(inf.read()))
    inf.close()
    outf.close()

if __name__ == "__main__":
    main()

Feel free to use, distribute and modify both.

like image 77
David Hanak Avatar answered Oct 30 '22 23:10

David Hanak


Adobe have recently released a new profiling tool for Flash called Adobe Scout:

http://gaming.adobe.com/technologies/scout/

It's a massive improvement on the old Flash Builder profiler - it gives you a detailed breakdown of CPU time, both for ActionScript execution, and internal player functions like rendering and networking.

It's free for a trial period - you just need to register for a free Creative Cloud account. After that, there will continue to be a free basic version, with the full version available as part of a paid Creative Cloud account.

like image 39
Michael Smith Avatar answered Oct 30 '22 23:10

Michael Smith


It's important to note that the Flash Player implementation is different on each platform and to an extent each browser, so expect notable speed differences. So if you're developing a resource intensive application you should be using profiling tools specific to each OS you're targeting, like for example Instruments on OS X and of course test the performance in each browser.

like image 45
dain Avatar answered Oct 30 '22 23:10

dain