我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”

原文链接:https://blog.uwa4d.com/archives/deepanalysis.html

今天的推文有点不一样,可以说并不是一篇严格意义上的技术文章,落笔成文的此时,我们即将完成第50次性能的深度优化。这50次深度优化的经历,让我们侑虎团队对性能优化有了更深刻的理解和感悟。今天我在此和大家分享,希望也能对大家自身项目的研发有所启发。


一、答案永远在现场

“我们游戏一个多月后就要上线了,但现在性能问题还是很大,现在我们在这块没什么头绪,你们是否有什么服务可以帮到我们?”

“我们知道你们侑虎为XX游戏做过深度优化,我们项目也想来用你们这个服务,请务必帮帮忙。”

“你们的报告分析得很赞,内存终于降下来了,这下我就放心了。接下来,我们团队会按照你们的报告继续优化其他地方。”

“你们分析的太细了,我们已经优化很久了,没想到还能找出这么多问题。”

这些是创业20个月以来,我们从游戏团队那里听到最多的几句话。上文所说的报告,是指我们侑虎科技的项目深度分析与优化方案报告。之所以写这篇文章,是因为我们即将完成我们的第50次游戏项目的深度优化。是的,你没看错,20个月50次,意味着平均一个月2.5次。平均每份报告100页、2.3万字。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第1张图片

对于一款游戏项目的深度优化,我们一般需要10天左右的时间全身心投入,其中包括2~3天的现场排查。这是因为很多时候,只有进驻现场查看代码,设身处地地与研发团队进行沟通,我们才能找到问题背后的真正答案。

举个例子,UI优化是一项非常复杂的工作。两个视觉表现完全一致的界面,其底下的性能开销可能完全不一样。究其原因,是其本身在Editor中的制作手法不同导致。

“动静分离”是我们经常提到的UI优化解决方法,但是知易行难,难在如何定位到底哪些动态元素造成了较高的开销,如何判断哪些开销是可以避免的,如何分离这些UI元素等等。所以,我们驻场的一个主要工作就是检测其UI界面的制作方式,将有问题的UI元素全部找出,并针对不同类型的UI元素给出不同的解决方案。同时,我们还会现场对UI界面进行一定的修改,告诉大家通过合理的优化后,项目的UI耗时应该是如何变化的。

下图就是我们在一款二次元ARPG项目中,UI优化前后WaitingForJob的性能对比开销。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第2张图片

再举个例子,堆内存泄露是大家项目中经常遇到的问题,这也是我们驻场时经常花费大量气力去解决的问题。一方面,我们需要通过阅读研发项目中的大量代码去定位可能存在的泄露点,因为很多时候,内存泄露都是因为Object被“到处索引”所致。另一方面,我们需要研发团队的配合,对代码进行完善并快速出包验证,这样反复调试后,泄露问题就能基本得到解决。

下图是我们对某项目的堆内存泄露问题优化前后的对比图,从发现到解决,一共不到24小时。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第3张图片

然而,如果我们没有到现场,会怎么样呢?

如果没有到现场,我们也许到现在都不知道为什么下图中的赛车(红框处)已经和外围的围栏和斜坡产生了碰撞,尽管实际上它们还相距很远。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第4张图片

如果没有到现场,我们也许到现在也无法告诉研发团队,如何仅用两小时,将下面的UI面板从38个Draw Call优化到9个Draw Call。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第5张图片

如果没有到现场,我们也许至今都未发现几个简单的CharacterController的center赋值会如此耗时。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第6张图片

如果没有到现场,我们也许至今都无法解释,在场景切换的过程中,为何会出现如此大量的“空闲”时间。


类似的如果非常多,所以我们必须到现场。答案在哪里,我们就在哪里。


二、别放过任何一个可疑的地方

别放过任何一个可疑的地方,因为那很可能是提升你项目效率的绝佳机会。

举个例子,我们在做UI优化时会做一个常规测试,就是把游戏静静地放在那里,不去操作,看看UI性能的变化。可以看到,在UI界面静止不动时,其对应函数的CPU占用仍然有23ms的开销。虽然耗时不高,但这已属于不正常的情况。所以,在到达现场后,我们详细检测了其耗时的具体根源,最后发现元凶是聊天框中的emoji表情:每隔12s都会转动一次,但由于其本身挂在一个很大的Canvas上(下图右),导致了每次emoji表情一改变,就会使得这个含有1804个顶点的Canvas完全重建一次,从而造成了较长的时间开销。

我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第7张图片
我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第8张图片

上述这种操作,也是UI界面中动静元素没有分离的一种典型例子。

再举个例子,下图是一个MMO项目某场景的一部分性能测试数据。值得注意的是,游戏在运行过程中改变了RenderSettings的参数,这一操作耗时巨大,703帧中耗时8600+ms,但我们在其他测试中却没有再发现该项耗时。这个反常的现象引起了我们的关注,所以我们在该场景中又跑了五组测试,每组大概15~20分钟,然而都同样没有发现这个开销。于是,我们请来了项目主程,通过制作上的沟通希望得到更多线索。当我们表达了我们对于这个问题的发现时,项目主程非常惊讶:“你们居然测到了这一项,但我真没想到这块会这么耗时。”原来,这个操作是用来改变场景的天气效果,而且一天只进行两次,一次只有30秒。



三、空杯心态,经验归零

我们正处于一个高速发展的时代,每个人的知识和经验在整个行业面前都可以说是微不足道。我们做过的优化越多,就越发现我们已知的渺小。有那么几个项目,我们在进驻团队前以为已经大致摸清了优化的方向,但等到了现场,面对他们的代码时,才发现我们之前推测的都是错的,一切都要推倒重来。

举个例子,下图是一款竞技类手游的动画系统在真机上的CPU耗时。可以看到,其每隔几帧,就会有一个20~30ms的CPU开销。


我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第9张图片

这显然是不正常的,进驻团队前,我们认为很可能是该项目中手动调用了一些Animator的API,按照经验来看,检查下代码应该很快就能定位问题。但没有想到的是,我们花了大量时间都未发现任何一行与动画模块相关的可疑代码。与项目团队的交流也否定了我们之前的想法。我们开始怀疑是不是我们的检测工具出了问题,但是项目中通过其他方式获取的运行帧率却又和这个开销非常吻合。更诡异的是,即便我们把动画角色屏蔽了,该耗时依然存在。这个时候,我们开始隐约觉得这个问题很可能不是出在动画模块上了。

既然问题不按套路出牌,那我们也就不能按照常规方法来解决了。第二天我们在开发团队的协助下,一起逐步对项目的各个模块进行拆分,看看这个问题到底出在哪里。具体经过曲折坎坷,就不赘述了。结果令我们相当惊讶,该CPU耗时居然与通过Android原生代码获取游戏的电量、Wifi Ping值的操作相关。当我们把该操作关闭后,一切都恢复了正常。下图就是正常的动画模块开销情况。

我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第10张图片

这个问题,我们后来在自己内部复盘时仍然觉得匪夷所思。我们也有对相关代码尝试进行复现,但是在我们的空项目中,一切正常。

这个例子不过是我们在现场遇到的众多幺蛾子问题之一。随着这些经验的累积,我们也渐渐发现,影响性能的已经不再仅仅是大家的代码编写问题、引擎API的使用问题,还会和所用的引擎版本、所处的硬件、OS版本,甚至设备的运行状态息息相关。

如果你耐着性子看到了这里,我敢说你肯定也遇到过以下这些情况:

1)一些问题只在我的项目中出现,而在空场景的项目中则没问题;
2)一些问题只在Android或iOS系统中出现,而在另一系统中则不会有;
3)一些问题只在某些移动设备上频繁出现,其他设备却都没有。

诸如此类的不确定性问题,在当今的项目研发中比比皆是。如何解决?将心态放空,将经验归零,多做实验,以数据说话。


四、没有标准的优化方案

我们经常会被问到:渲染的耗时怎么下降、UI模块怎么优化、如何提升加载效率等问题。对此,我们的回答往往是:

没有标准的优化方案,只有最适合你项目的优化方案。一切的解决办法都是围绕着具体问题而产生的。

举个例子,渲染模块开销高,其优化的前提是一定要定位清楚,究竟是什么造成它这么高的耗时,瓶颈是不透明渲染、半透明渲染、裁剪、图像后处理还是其他?只有定位准确了,才能制定出行之有效的优化方案。正所谓,对症下药,才能药到病除。

再举个例子,大家都知道Draw Call越高,开销越大。但是否越低越好呢?当然不是。UI Draw Call就是一个很好的例子。在一个项目中,我们建议研发团队将UI的整体Draw Call从当前的9拆成20。研发团队很不理解,DrawCall多了不是耗时更多?确实如此。但是该团队为了降低Draw Call将大量UI元素拼合在一起,虽然渲染开销下降了,但UI重建的开销却大大提升了。在我们来看,当UI Draw Call拆到20时,虽然渲染模块的开销可能会平均增加1ms,但UI重建的平均开销则会下降5ms。从整体性能而言,至少能有4ms的提升。


五、压力越大、收获越大

性能优化本身就是一件很难的事,它一般需要优化人员具有很扎实的功底、很全面的基础以及很丰富的实战经验。而如果要在一个完全陌生的项目中,十天内就给出一份详尽的性能优化方案,则更是难上加难。

回想我们这么多次的现场实战中,没有一次是顺风顺水的,在每个项目中都会遇到一些意想不到的技术问题。甚至有几次,我们内部已经做了决定:“如果今天晚上这个问题还是找不到根源,我们就退款走人”。这就是我们每一次都要面对的压力。幸运的是,每一次我们都跨了过去。同时,每一场深度优化后,我们都会进行复盘,这次优化中哪些地方还有待提升,就尝试能否继续打磨我们的优化工具,缩短优化的时间;哪些地方做得很高效,就看看能否复制到线上的测评工具中去,让更多团队受益。

当然,我们每次收获的不止是经验,更重要的是又多了一些信任我们的朋友。彼此的信任,让我们的脚步能够涉足更多的地方,看到并解决更多的技术问题。

曾经有位朋友和我说,“优化的方法茫茫多,然而针对于我们项目的优化方法在千种万种中可能只有一个”,这也许是大部分游戏从业者的痛点。所谓”优化“,能够解决“你的问题”才是优化。

PS:时间过的好快,一晃创业已经二十个月了。下图是2015年因为一个感触给自己加的一条梦想。现在在机场、火车站、地铁上甚至是电梯里都可以时不时看到我们优化过的游戏了。所以梦想还是要有的,万一实现了呢!

我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”_第11张图片

这是我们第一次尝试以故事的形式和大家分享了一些深度优化的心得,希望能对程序、策划、美术、制作都能有所启发。如果上述的文字有幸能契合你的某些想法,亦或许你也有一些深刻的经历愿与大家共享,我们欢迎你在下方留言。

你可能感兴趣的:(我们用50次游戏性能的深度优化,总结出了五条“毒鸡汤”)