我在大厂如何做性能优化

  大约5年前,我加入一个大厂的MMORPG手游项目,彼时那个项目已经研发了2-3年,正处于冲刺上线的关键期,那是一段忙碌的日子,回想起那段岁月,依然心潮澎湃。

  领导一看我是老司机,便安排我去做优化,优化3个方面:优化CPU性能,优化内存占用,优化启动速度。

  我打开代码看了几天,觉得不难,因为跟我做过的第一款游戏大同小异,只是之前那款是多线程,一个线程承载几个场景(可配置),而在研的这款,逻辑处理是完全的单线程。

  显然,逻辑单线程设计不合理的,不能充分利用CPU多核优势,相当于长着12根手指,却只能用一根手指头敲键盘。

  照理说,我只要把架构按之前那款的样式改造一下就行,但领导说不行,大家已经习惯这个架构,害怕大的架构修改影响进度。

  我说,那一个服务器上多部署几个GameServer实例,充分利用CPU多核优势,这样也行。领导又说不行。好吧,你官大你说了算。

  在优化之前,我做了一次profiling,大概单机同时承载800人,内存占用8G多,启动速度90秒,这个数据我至今记得很清楚(如果我真的记错了,请看到这篇文章的前同事提醒我)

  先优化CPU,我用gprof2dot.py把callgraph画出来了,这玩意儿很清楚,相比而言,火焰图就是渣渣。

  对照图,把CPU消耗top函数找出来了。

  对照top list,一个个分析,合理不合理?有些函数cpu消耗虽然大,但是它合理,有些虽然消耗小,但是不合理,因为它可以更少。

  由于时间久远,很多细节,我已经不记得了,只写还记得的几点。

  GameServer有一个逻辑处理大循环,里面调用tick(),然后游戏里的几乎所有逻辑都是在这里执行,因为我们会在tick里记时,为确保精度,tick一秒钟至少要被调用10次以上,心跳驱动其实就是GameServer的一个动力源(另一个是网络协议驱动)。

  首先,这是轮询的方式,比如玩家一秒钟回一滴血,会在tick里遍历所有玩家,依次调用每个玩家的tick,玩家的tick里会用时间戳做差的方式检查是否过了一秒,轮询很频很费,是一种比较low的方式,完美世界架构跟天龙八部架构最大的差别就是完美架构没有tick。

  另外,所有的逻辑都放在tick里,其实很多逻辑没有必要这么高精度,大量的超时判断很耗费cpu,所以,我做的一个修改就是分频,在tick里调用tick1Sec,tick10Sec等等,再把那些逻辑挪到不同的tickXSec里去。

  也就是有些函数1秒间隔调一次就行,有些10秒调用一次就行,而之前所有要在tick里处理的函数都是每秒至少调用10次。

  这个方式很有效,具体多有效,我记不清了。

  另一件事情,就是inline,GamePlayer上有很多GetX,GetY类似的方法,这些短小的Getter遍布各处。

  大家没有用inline的习惯,或者在源文件函数定义加inline,而函数声明不加,这相当于内裤外穿,因为要inline必须在调用的地方见到函数定义的全貌。

  函数的调用开销和inline的原理我不想展开,我必须向团队成员解释,并说服大家以后不要再这么干,我哼哧哼哧把这些低效的写法都改了过来。

  然后就是属性计算,属性刷新,游戏中,玩家的属性变化了,会影响其他属性的变化,属性变化后,需要同步到客户端,这块我记得我也改了一波,总体思路就是用延迟计算,增量更新。

  然后就是核心数据结构和基础库,我基本上改了个底朝天,领导虽然忧心忡忡,但好像还是默许了我的做法。

  内存分配,对象管理,这些也改了,都是一些常规动作,内存池,对象cache,利用局部性,效果上不错。

  再有就是内存拷贝,特别是在协议处理那块,我好像也动过,我非常警惕内存多次拷贝,另外,我好像把IO和逻辑线程做了分离,这样对业务逻辑没有影响,但是逻辑线程的工作量降下来了。

  还有很多细枝末节,但又有必要的优化,记不清也说不清了。

  说一下最终效果吧,从800提升到了至少3000。

  优化了CPU,就搞内存。

  有个哥们,完全就是瞎搞,本来只需要几兆内存,他硬是申请了几百兆,被我怼得萎靡了几天。

  其他的,反正也是各种细致分析,比如减少内碎片,直接说结果,把内存占用从8G多降低到4G多,整整降低一半。

  启动时间,这块,他们之前也搞过,束手无策。

  我为每个启动步骤加了时间度量,然后一点点细究,记忆深刻的几点就是利用C++的placement new,把内存分配跟对象构建分离,之前一启动就把所有对象都构建起来,对象构建函数很大很耗费,改了之后,按需分配,启动速度直线飙升,当然还有其他手段,零零散散,记不清了,也说一下结果,从90秒降低到6秒多。

  讲道理的话,无论是CPU还是内存占用还是启动时间,效果都不错,然后我拿这个去答辩,没说几分钟,就被面试官喊咔,常规手段,OUT!好吧,打扰,告辞!

  其实优化是个细致活,是非常detail非常detail的事情,一点点试,一点点改,它有一个基本要求,就是做优化的人,本身代码功底很好。

  这里有几个误区,觉得性能优化很高大上,然后很多人喜欢tree new bee,说什么汇编优化,cache优化,i-cache,d-cache,这,哪,其实大部分都是忽悠人,我说句实话吧,大部分优化用不着,你只需要把那些低级错误找出来,改正,就OK了。极致优化其实收益很少,都是忽悠领导用的。

  第三,就是不要把优化当成很容易的事情,曾经,我LP被领导安排去做优化,我把所有的方法都告诉她了,她也按我说的去做了,但是最终效果那啥,你懂的。

  就这样吧,这些优化经验分享给有缘人,感谢围观。

上一篇:【喜讯】Apache DolphinScheduler 荣获 “2020 年度十大开源新锐项目”


下一篇:easyblink