大型Flash项目性能优化:关注0ms方法

Flash大型项目尤其是多人同时在线的webgame,性能优化是一个很重要的部分。性能优化大概就是两个部分:内存优化、CPU优化。所谓CPU优化,说白了就是降低CPU使用率。我们首先要解决的一个问题是:让代码不要做当前不需要做的事

最近我们的项目(MMOARPG)加东西比较多,从Windows资源管理器看CPU,即使站在场景中什么都不干,CPU使用率竟然还在20%+。所以描述一下问题就是:

问题描述:找出让CPU空转的Function。

于是去查那些帧循环里的方法,看看是不是耗时太长。一般耗时2ms以上的,我们都会想办法优化。

场景中有NPC和怪物,我们要查一下他们的定时器:

EnterFrameManager.getInstance(ANIMAL_TIMER).add(frameScript);
private function frameScript():void
{
    var start:int = getTimer();

    // something may cost time
    // code

    var cost = getTimer - start;
    trace("[$ms] frameScript : Animal".replace("$", cost));
}

结果偶尔打出来一些:

[0ms] frameScript : Animal
[1ms] frameScript : Animal

看上去不是这方面的问题。过了几天,在厕所里抽着烟,猛然悔悟了一下,立刻回去查一下EnterFrameManager。顺带说一下,当我们有很多帧循环或者Timer事件要注册,通常写一个FrameManager或TimerManager来做。在Manager里统一响应Event.ENTER_FRAME,处理绑定的所有事件。EnterFrameManager这部分简化后的代码如下:

public function add(script:Function):void
{
    var o:Object = {"func": script};  // you may want to add some other attributes
    scripts[script] = o;
}
private function enterFrameHandler(e:Event):void
{
    for each (var i:Object in scripts)
    {
        i.func();
    }
}

我们给超过30ms的enterFrameHandler加上执行时间,看看一帧那么宝贵的时间里,耗时的部分在哪。

private function enterFrameHandler(e:Event):void
{
    var s:int = flash.utils.getTimer();
    for each (var i:Object in scripts)
    {
        i.func();
    }
    var cost:int = flash.utils.getTimer() - s;
    if (cost > 30) {
        trace("[$ms] enterFrameHandler".replace("$", cost));
    }
}

果然发现一些超过30ms的帧循环:

[32ms] enterFrameHandler

然后我们加一些代码看看这些帧循环里都搞了哪些破事儿:

public function add(script:Function):void
{
    var o:Object = {"func": script};  // you may want to add some other attributes
    o.name = getFunctionName(script);
    scripts[script] = o;
}
private function enterFrameHandler(e:Event):void
{
    var s:int = flash.utils.getTimer();
    for each (var i:Object in scripts)
    {
        var ss:int = flash.utils.getTimer();
        i.func();
        i.cost = flash.utils.getTimer() - ss;
    }
    var cost:int = flash.utils.getTimer() - s;
    if (cost > 30) {
        trace("[$ms] enterFrameHandler".replace("$", cost));
        for each (var i:Object in scripts) { 
            trace("[$ms] ".replace("$", i.cost) + i.name); 
        }    
    }
}

输出的结果让人吃惊,32ms的enterFrameHandler里,居然有大量0ms的方法,超过40个:

[32ms] enterFrameHandler
[0ms] frameScript 
[0ms] frameScript 
[0ms] frameScript 
[1ms] frameScript 
[0ms] frameScript 
[0ms] frameScript 
....省略
[0ms] frameScript

这是什么意思呢?就是说,很多执行了0ms的方法,加起来的时间超过了30ms。经查,有很多NPC和场景特效,虽然出了视野,但是帧循环没有移除。当主角看不到这些NPC和场景特效的时候,这些循环还在跑。虽然执行时间很短(不超过1ms),但是加起来不容忽视。0ms方法加起来不是0ms!一帧的时间也就40来毫秒(24fps),这些空循环偶尔就占了30ms,难怪帧频会降低,难怪会占CPU,失误啊。

解决办法:果断在NPC和场景特效出视野的时候,移除帧循环frameScript,问题解决。

结论

在大型项目中,不要忽视了帧循环里的看上去不那么耗时间的函数,他们的执行时间可能只有0ms,但这只是因为我们测不准小于1ms的时间。即使你说,我这些方法里什么都没干,他们也可能是因为函数调用栈深度问题而消耗了时间。当有很多这样的方法时,它们众志成城,团结的力量就会给CPU带来压力了。我们应该去掉那些没用的帧循环,需要的时候再加上。

延伸

这次比较幸运,出视野之后帧循环还可以清掉。但如果很多玩意儿都在视野里呢?CPU是省不来的。想让玩家获得最好的体验,我们应该做的是让CPU保持稳定,也就是让帧频保持稳定。我认为,换句话说,在一个帧循环里不要干太多事儿。这个怎样在策略上优化呢,回头再总结这方面经验。

付上网上找的获得方法名称的函数:

private function getFunctionName(fun:Function):String{
    try{
        var k:Sprite = Sprite(fun);
    }catch(err:Error){
        var fn:String = err.message.replace(/.+::(\w+\/\w+)\(\)\}\@.+/,"$1");
        return fn==err.message?(err.message.replace(/.+ (function\-\d+) .+/i,"$1")):fn;
    }
return null;
}
 
引用自:http://jsfox.cn/blog/webgame/webgame-project-cpu-optimization-about-0-millisecnd-function.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值