如何快速优化手游移动端性能优化问题问题

腾讯大咖说:腾讯是如何做Unity手游性能优化的 - 简书
腾讯大咖说:腾讯是如何做Unity手游性能优化的
本文由腾讯WeTest授权发布
作者:chunhe,腾讯资深后台开发工程师。
著作权归作者所有。商业转载请联系WeTest获得授权,非商业转载请注明出处。
俗话说,用户体验不谈性能就是耍流氓。 在PC游戏上的性能问题并没有那么明显,加个内存换个CPU或者刷个主频就能轻松搞定;到了手游时代后情况则显得比较严峻,捉襟见肘的内存使得资源加载时如履薄冰,加上高中低不同配置的机型让性能问题显得更加突出,一个低端机型上的卡顿就可能造成一大批屌丝用户的流失,这当然无法被忽视。
在手游的浪潮之巅,腾讯对于手游品质的要求从1.0到2.0再到现在的3.0,不仅是玩法和内容,在游戏质量的审核上也始终如一的保持着高要求高标准。腾讯游戏的品质管理中心在Unity手游性能上进行了更深层次的挖掘,这是一个腾讯内部非常受欢迎的性能分析产品,无论你是否正在从事Unity相关的工作,听完这个良心产品的故事保证会让你增加90%的魅力值。但在此之前、先要看看你的“性”能到底行不行?
(下文有大量专业术语,有可能引起您的不适,请在家长指导下阅读。)
一.常见的Unity手游性能问题有哪些?
Unity手游的性能问题一直是被业内视为诟病,腾讯公司内部的TDR评审就是一个专门针对技术细节进行专家团评估的环节;早期的TDR评审关注的是内存是否超标、CPU是否饱和、网络流量是否过大等数据,经过近几年手游浪潮的洗礼,现在评审过程中会更加注重细分问题的研究和排查。
这里写图片描述
如果说左边是玩家经常会遭遇到的表面现象,那右边则是基于Unity引擎深挖后的问题本质。 它们对游戏的具体影响是什么呢?就拿最近比较火的《王者荣耀》来举例,我们有幸参与了它上线前后的几个优化版本的分析,先后遇到过的问题和优化方法主要有下面几个:
这里写图片描述
1.由于实时对战游戏的数据包数量巨大,早期版本的帧同步策略会导致比较明显的卡顿,通过进行数据包的合并与优化逐渐解决了卡顿问题;
2.频繁创建和销毁的小兵对象让CPU爆表了,大量的小兵如果采用实时内存的分配和回收,会产生大量的内存碎片和系统开销,解决方法之一就是采用高效的对象池进行优化,对每个内存对象的状态进行操作即可;
3.性能分析过程中,发现单人同屏和多人同屏时的开销都很大,通过视野裁剪技术,使得玩家视野外的不必要的特效和渲染可以全部关闭,极大降低了CPU、GPU和内存的开销;
4.在高中低三档机型上玩游戏时,分别加载不同层次的特效包,这也有助于降低CPU和内存的开销;
5.游戏内界面采用了UGUI的方式实现,但大量的实时UI变化使得副本内每帧会有230以上的drawcall,导致中低端机型感受到明显卡顿,最终采用UGUI+自研究UI的组合拳,重写了一套紧密结合游戏自身特性的UI来实现战斗血条和浮动文字的效果。
二.手游发布之前的性能分析
近年来,经过若干惨痛的教训之后,业内已经逐渐意识到手游性能已成为了生死存亡的关键,特别是对于做大DAU的手游来说尤为重要。腾讯对于手游性能的测试和监控也是多管齐下,在新版本发布之前会再三确认性能是否符合发布标准,拿王者荣耀这款实时竞技游戏来说,在测试阶段会采集大量的性能数据进行分析,测试经理对各项性能指标进行评估并给出最终质量结论。
这里写图片描述
如上图所示,首先,功能测试也就是通常所说的人肉测试,用于测试游戏的新、老功能点,测试工程师在工作过程中可以使用进行数据采集;自动化测试则是基于腾讯WeTestgautomator自动化框架来实现,功能类似于Robotium,在无须人力参与的情况下能覆盖到绝大部分技能、角色和关卡;灰度发布指的是在一个很小范围定点推送手游的新版本,并观察运营期的质量情况和玩家反馈。无论是哪种测试方法,在过程中都可以用进行数据采集,在测试完成后,服务器会自动进行数据分析并给出多项性能数据结论;这些性能数据的结论来自于Unity官方的推荐标准值和腾讯游戏海量的经验库,如果同意机器给出的结论则可以巩固当前算法,当然也可以挑战自动分析的结论,帮助后台改进算法,最终版本质量结论还是来自于测试经理的判断。
看到这里是不是有一个疑问:不做性能分析行不行?当然行,并且你的产品照样能发布、能上线,带来的结果就是用户抱怨用户投诉用户流失。生病了、还得老老实实的去看病去吃药;冰冻三尺非一日之寒,一场大病的费用远比日常保养要贵的多,对应测试行业的名言就是“bug发现的越早、修复成本越小”。
三.工具的简介
目前市面上大多数性能工具都还停留在操作系统级别的数据上, 在游戏自身的分析上似乎还缺少点什么,所以腾讯的工程师们觉得还可以往灵魂深处挖一挖,于是就研发了这个手游性能分析工具,可以让用户以最小的成本在真机上进行游戏性能深度分析。常见的游戏质量改进的过程如下图,能帮你完成的是前两个环节,至于第三步、解决问题当然还是要开发人员去改代码了。
这里写图片描述
通过的深度分析,能够帮助开发者发现当前游戏内分类资源的占用情况。
这里写图片描述
如上图所示,在资源分析纬度上可以给出如下结论:· 资源使用总量是否在合理范围之内。· 一个场景内的资源重复率。· 资源对象拷贝的数量是否合理。· 场景切换时保留的资源详情。· 网格、纹理、音频、动画、GameObject等资源是否超标。 在性能分析纬度上,以腾讯的TDR标准为例,在高中低三档机型上会有不同的标准,在三档机型中做了自动的筛选和判定,便于开发人员能更加直观的发现问题。(如下图)
这里写图片描述
首先、在游戏场景内对于FPS、CPU、PSS的变化趋势是需要重点关注的;其次、对于mono这种只增不减的东西,当然也是关注的重点,mono堆内存的不断分配会直接导致PSS内存增长且不可逆;再次、对于和渲染有关的drawcall,也是手游需要关注的性能指标之一,drawcall太高会导致FPS陡降,造成视觉上的卡顿。
四.同类工具对比MAT
(Memory Analyzer Tool)的缺点:
l 需要导入HPROF文件再分析;
l 只能查看java层的内存情况,看不到native堆的详情; xcodeinstrument 的缺点:
l 只能用于mac,ios;
l 只能查看C++ 或 object C 的情况,看不到mono堆的详情;
Unity Profiler 的缺点:
l 需要单独编译develop版本;
l 在PC上执行,没法捕获真机数据;
l 内存数据跟实际真机的数据差异很大、多的时候有几十M差距;
l 只能看到最近一段时间的数据,看不到总体的详情;
对于Unity大神和开发人员,你更关心的应该是详细的性能数据,都能满足你们。大神会说“我更喜欢看着Unity profiler直接调试啊”,那你还得腾出时间编译一个develop版本、还得重新跑一遍游戏、数据和真机还相差很多,关键是大神哪来那么多时间呢?
这里写图片描述
所以答案是肯定的,日常测试工作中加入了数据采集和数据分析功能,就可以提高很大的工作效率。
我们常见的产品质量改进流程无非是下面这四步:
测试人员发现问题;
提bug 给开发人员;
开发人员编译develop版本;
开发人员用Unity profiler 定位原因;
用进行游戏测试能帮你省掉后面2个步骤,何乐而不为呢?通常情况下,开发人员是间隔几个星期甚至几个月才会去做一次性能调优的工作,中间已经隔了N个版本,有很多问题会被埋的很深;基于“问题发现的越早修复成本越小”的硬道理,功能测试人员完全可以用进行日常的版本功能测试,让在后台默默的为你发现各种性能问题。
l 即插即用、无须编译无须嵌入SDK、真机运行数据;
l 提供mono内存分配信息和mono快照对比;
l 能看到整个测试流程中的所有数据,而不仅仅是某一段时间;
l 被误操作产生的对象拷贝数量;
l 函数开销排名;
l 关卡间保留的冗余资源;
五.性能优化的N种武器作为一个以性能优化为己任的工具类产品,不仅致力于问题的发现和定位,也希望为开发人员提供更多更实用的性能优化方法。
l 控制贴图大小,尽量不要超过 ;
l 尽量使用2的n次幂大小的贴图,否则GfxDriver里会有2份贴图;
l 尽量使用压缩格式减小贴图大小;
l 若干种贴图合并技术;
l 去除多余的alpha通道;
l 不同设备使用不同的纹理贴图,分层显示;
l 尽量控制模型的面数,小于1500会比较合适;
l 不同设备使用不同的模型面数;
l 尽量保持在30根骨骼内;
l 一个网格不要超过3个material;
l N种动画压缩方法;
l 尽量减少骨骼数量;
l 采用压缩MP3 和 wav;资源方面的优化:
l 使用 Resource.Load 方法在需要的时候再读取资源;
l 各种资源在使用完成后,尽快用Resource.UnloadAsset和UnloadUnusedAsset卸载掉;
l 灵活运用AssetBundle的Load和Unload方法动态加载资源,避免主要场景内的初始化内存占用过高;(实现起来真的很难…)
l 采用www加载了AssetBundle后,要用 及时释放;
l 在关卡内谨慎使用DontDestroyOnLoad,被标注的资源会常驻内存;
代码的优化:
l 尽量避免代码中的任何字符串连接,因为这会给GC带来太多垃圾;
l 用简单的“for”循环代替“foreach”循环;
l 为所有游戏内的动态物体使用内存对象池,可以减少系统开销和内存碎片,复用对象实例,构建自己的内存管理模式,减少Instantiate和Destory;
l 尽量不使用LINQ命令,因为它们一般会分配中间缓器,而这很容易生成垃圾内存;
l 将引用本地缓存到元件中会减少每次在一个游戏对象中使用 “GetComponent” 获取一个元件引用的需求;
l 减少角色控制器移动命令的调用。移动角色控制器会同步发生,每次调用都会耗损较大的性能;
l 最小化碰撞检测请求(例如ray casts和sphere checks),尽量从每次检查中获得更多信息;
l AI逻辑通常会生成大量物理查询,建议让AI更新循环设置低于图像更新循环,以减少CPU负荷;
l 要尽量减少Unity回调函数,哪怕是空函数也不要留着;(例如空的Update、FixedUpdate函数)
l 尽量少使用FindObjectsOfType函数,这个函数非常慢,尽量少用且一定不要在Update里调用;
l 千万一定要控制mono堆内存的大小;
性能优化就像海绵中的水,又或是内衣里的肉,挤一挤总会有的。同时,性能优化并不是一劳永逸的工作,而是一个漫长而具有挑战的任务;项目的各个阶段都会有性能上的问题,在用户体验的基础上持续进行打磨,持续保持产品的良好性能才能赢得好口碑。(和保持身体健康是一个道理)
这里写图片描述
Unity手游的性能优化过程更像是一门时空转换的艺术, 持续在CPU和内存之间取得一个平衡。空间不足时则需要释放一些无用数据,以获得更优的空间使用率;时间太长时就需要降低不必要的函数开销。例如在低端机上,为了节约有限的内存空间,静态加载的资源会相对较少,很大一部分资源通过动态加载和释放;而在高端机上则不用考虑空间的限制,可以一次性静态加载更多的资源,省去了不少loading和GC的工作,让游戏体验更加流畅。
Unity目前已经可以使用。
体验地址:
关于测试报告的问题:
使用帮助:
常见问题: -
2017年起,随着“王者荣耀”等热门手游的走红,热门手游和热门手机之间的互动和博弈越来越有看点。热门手游正在逐渐影响到手机的产品设计、营销方式,而热门手机也在倒逼热门手游的产品团队针对特定产品进行优化。 手游成了检验手机厂商的试金石 热门国民手游的爆发,在很大程度上在检验手...
本文通过对内存泄漏(what)及其危害性(why)的介绍,引出在Unity环境下定位和修复内存泄漏的方法和工具(how)。最后提出了一些避免泄漏的方法与建议。 在之前推送的文章《内存是手游的硬伤——腾讯游戏谈Unity游戏Mono内存管理及泄漏问题》中,已经对腾讯游戏在Un...
作者:Arthuryu,腾讯高级开发工程师著作权归作者所有。商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。 WeTest导读 本文通过对内存泄漏(what)及其危害性(why)的介绍,引出在Unity环境下定位和修复内存泄漏的方法和工具(how)。最后提出了...
//我所经历的大数据平台发展史(三):互联网时代 o 上篇q.com/cn/articles/the-development-history-of-big-data-platform-paet02 编者按:本文是松子(李博源)的大数据平台发展史...
WeTest导读 内存是游戏的硬伤,如果没有做好内存的管理问题,游戏极有可能会出现卡顿,闪退等影响用户体验的现象。本文介绍了在腾讯游戏在Unity游戏开发过程中常见的Mono内存管理问题,并介绍了一系列解决的策略和方法。 什么是Mono内存 对于目前绝大多数基于Unity引...
先说一个无关的小故事,不过这是一个真实的故事。大概在两千零几年的时候吧,那个时候兴起一场养藏獒的热潮,加上各种小说故事的推波助澜,人们对藏獒这个品种趋之若鹜。好多狗商、狗场都开始了藏獒的养殖,连那时非常有名的一个国字号教练都解散了运动队,用比赛奖金养起了藏獒。那个时候一只纯...
1.行动之前先要考虑清楚,上完厕所,千万别用荆棘来擦屁股! 2.、不要去追查会伤害自己的一面。日子对每一个人来说,都是如此艰难,我们只有接受变化,适当忘记,适当忽略,只有自己对自己好一点儿。 3、所有的错误,我们都知道,然而终究改不掉。能改的,叫做缺点,不能改的,叫做弱点。...
假设有一个需求是用户给评论点赞,数据库设计是三张表,用户表t_user,评论表t_comment,点赞表t_praise,其中点赞表中有两个外键分别是user_id和comment_id,分别关联用户表的用户id和评论表的评论id,然后规定一个用户只能给同一条评论点赞一次,...
当你越来越迷恋“治愈系”的时候,当你被某个场景萌化了的时候,当你渴望一个肩膀、或者一个拥抱的时候,……有没有想过这究竟是为什么? 其实,我也直到最近才有了答案 偶然间看了比利时动画电影《魔法总动员》(个人更喜欢另一个直译的名字:《魔法之家》),魔术师劳伦斯的与那些魔术道具间...如何快速优化手游性能问题_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
如何快速优化手游性能问题
我有更好的答案
这样可以减少图片的存储空间和占用内存。(UGUI的sprite packer会自动处理这种情况)2,有很多情况会触发Canvas对网格的重建,例如Image,Text等UI元素的Enable及UI元素的长、宽或Color属性的变化等。Canvas中UI Mesh顶点较多的话:同一个UI界面的图片尽可能放到一个图集中,这样可以尽可能的降低drawcall:1. 智能的Sprite packer可以将图片按tag自动生成图集而无需人工维护,etc1的好处是第个像素点只战用0,则该项将会出现较高的CPU开销。在Unity的Profiler中则对应的是Canvas。对于废弃的UI图片资源.com/neoliang/FindUnUsedUITexture" target="_blank">https,下载地址:https://bitbucket.org/Unity-Technologies/ui/src相比于NGUI,例如通用的弹框和按钮等,UGUI有以下几个优点一、UGUI简介UGUI是Unity官方推出的UI系统,集成了所见即所得的UI解决方案, 其功能丰富并且使用简单,不管你使用UGUI还是NGUI)。1.合理的分配图集合理的分配图集可以降低drawcall和资源加载速度;具体细节如下;相同功能的图片放到一个图集中, 例如装备图标和英雄头像等;这样可以降低切换界面的加载速度。不同格式的图片分别放到不同的图集中,例如透明(带Alpha)和不透明(不带Alpha)的图片,并且非常方便多分辨率屏幕自适配,unity会将resource目录下文本全部打成一个大的AssetBundle包(非resouce目录下的文件只有在引用到时才会被打到包里),从而出现冗余.SendWillRenderCanvases或Canvas.BuildBatch占用过多的时间。Canvas.BuildBatch主要功能是合并Canvas节点下所有UI元素的网格,合并后的网格会缓存起来,只有其下面的UI元素的网格发生改变时才会重新合并。而UI元素的网络变化主要是因为Canvas.SendWillRenderCanvases调用时,rebuild了Layout或者craphic。该函数的调用过程时序图如下:该过程由CanvasUpdateRegistry监听Canvas的WillRenderCanvases(上图中1)而执行,主要是对前标记为dirty的layout和craphic执行rebuild。引起layout和graphic的dirty主要原因是因为Canvas树形结构下的UI元素发生了变化(例如增加删除UI对象,UI元素的顶点,rec尺寸改变等)调用了Graphic.SetDirty(实际上最终都会调用CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild)。在rebuild layout之前会对Layout rebuild queue中的元素依据它们在heiarchy中的层次深度进行排序(上图中的2),排列的结果是越靠近根的节点越会被优先处理。rebuild layout(上图中的3),主要是执行ILayoutElement和ILayoutController接口中的方法来计算位置,Rect的大小等布局信息。rebulid graphic(上图中的4),主要是调用UpdateGeometry重建网格的顶点数据(上图中5)以及调用UpdateMeterial更新CanvasRender的材质信息(上图中6)。基于以上UGUI的网格更新原理,我们可以做以下优化:a.使用尽可能少的UI元素;在制作UI时,一定要仔细查检UI层级,删除不不必要的UI元素,这样可以减少深度排序的时间(上图中的2)以及Rebuild的时间(上图中的3,4)。b.减少Rebuild的频率,将动态UI元素(频繁改变例如顶点、alpha、坐标和大小等的元素)与静态UI元素分离出来,放到特定的Canvas中。c.谨慎使用UI元素的enable与disable,因为它们会触发耗时较高的rebuild(图中的3、4),替代方案之一是enable和disableUI元素的canvasrender或者Canvas。d.谨慎使用Text的Best Fit选项,虽然这个选项可以动态的调整字体大小以适应UI布局而不会超框,但其代价是很高的,Unity会为用到的该元素所用到的所有字号生成图元保存在atlas里,不但增加额外的生成时间,还会使得字体对应的atlas变大。,其CPU性能开销高主要原因之一是Canvs对UI网格的重建。这样做虽然在运行时不会对cpu造成太多负担,贴图)等失效,如果这些文件放在resource目录下,在打包时,一般在进入关卡时,都会手动释放外围系统的资源,以便使关卡内有更多的内存可以使用,或许你会觉得UI的制作规范及指导方法与优化无关,需要加载大量的角色及场景资源,内存比较吃紧,生成的图集合并方式比较合理,只是将失效的节点及动画disable了,笔者也写了一个扫描废弃脚本的工具,代码地址. 所见即所得的编辑方式,然后再想办法降低它的消耗。经过笔者多次使用profiler对UGUI的分析来看,而加载这样的对象也比较耗时、引用了过多的图集以及加载了不必要的资源等。如果从设计和制作UI一开始就遵守特定的规范,则可以规避不必要的性能开销。笔者根据参与的多个项目总结了以下几点通用的规范和指导方法(这些规范适用于所有项目,找到消耗最高的函数,材质等)应放到resource目录之外因为随着项目的迭代,可能会导致部分资源(动画.在android设备上使用etc格式的图片目前;另外,对于废弃的脚本,可能还会有某些对象持有对它的引用,但是在Editor模式下仍然会打到图集中从而影响优化决策。笔者写了一个扫描未使用到UI贴图资源的工具,代码地址,而靠近叶子节点显示在顶层;这样的渲染方式使得调整UI的层级比较方便和直观。4. RectTranForm及锚点系统更适合于2D平面布局.关卡内的UI资源不要与外围系统UI资源混用在关卡内,增加不必要的存储空间和内存占用:///neoliang/MissingFinder三、CPU优化一般来说,优化cpu性能应该先用profiler定位到性能热点.meta|meta)$&例如在笔者的一次扫描中,发现在了如下结果.com/neoliang/MissingFinder" target="_blank">/neoliang/FindUnUsedUITexture。共用的图片放到一个或几共享的图集中,在Scene窗口中即可编辑。具体的细节可参考:6:<a href="https://github,无冗余资源,几乎所有android设备都支持etc1格式的图片,虽然未放到Resource目录最终不会打到包里,但是在加载时会增加不必要的加载时间以及内存占用.删除不必要的UI节点、动画组件及资源随着项目的迭代:3,其它非prefab文件(例如动画,贴图. 渲染顺序与GameObject的Hierarchy顺序相关,靠近根节点显示在底层.5个字节而普通rgba32的图片每个像素点占4个字节,也就说一张图片如果使用rgba32的格式所占用的内存为4M而etc1格式所占用的内存仅为0,可以和美术同学商量适当的降低图片的精度。如果战斗内的UI与外围系统的UI使用相同图集里的图片,则有可能会使得外围系统的图片资源释放不成功。对于关卡内与外围共用的UI资源需要特殊处理,一般来说复制一份出来专门给关卡内使用是比较好的选择。4.适当的降低图片的尺寸有时UI系统的背景可能会使用全屏大小的图片,比如在Iphone上使用大小的图片;使用这样尺寸的图片代价是很昂贵的。可以通过以下代码(Mac环境下)在控制台窗口中查看当前目录下所有非prefab资源的代码:find . -type f | egrep -v &(prefab|prefab。二,同时其源代码也是开放的
采纳率:92%
来自团队:
为您推荐:
其他类似问题
您可能关注的内容
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;后使用快捷导航没有帐号?
 论坛入口:
  |   |    |   | 
查看: 4979|回复: 0
产品优化不再盲目!一次MMO手游性能诊断全纪录
文/侑虎科技
今天我们将以一款MMO手游为例,分享UWA技术诊断的全过程,希望能对大家的开发有所借鉴,同时也期待大家能从根本上逐渐掌握性能优化的思路,从此优化不再盲目。
image001.png (274.23 KB, 下载次数: 43)
18:20 上传
我们将从优化的几大性能参数如CPU、内存、GPU出发,通过这些模块在UWA报告中展现的性能数据,结合报告独创的优化功能,为大家梳理出一条逻辑清晰、全面详实的优化思路。
◆◆◆◆CPU模块
1. 概览(定位CPU耗时的瓶颈)
我们先来看下CPU的平均耗时占用情况,在测试的十多分钟内产生了14539帧数据,其中帧数大于33ms的CPU耗时占比是44.2%,而一般我们推荐开发者将这指标尽量控制在10%以下来保证游戏整体的流畅度。
image003.png (182.47 KB, 下载次数: 41)
18:20 上传
从上图中CPU走势上来看,战斗过程中有比较大的波动,下图是各大模块的耗时占比,可以看到占比较大的是渲染模块(42%),其次是脚本(39%),动画(4%),加载等。
image005.png (116.5 KB, 下载次数: 38)
18:20 上传
值得大家注意的是,脚本模块的统计中不仅包含了逻辑代码的开销,还包括了UI模块的开销,因为NGUI、UGUI都属于脚本的开销。由于CPU模块中的渲染、脚本、动画模块开销比较大,所以后续我们将对这些模块进行比较详细的解读。
2. 渲染模块 & 粒子系统
从渲染模块的数据可以看到,半透明渲染均值为5.7ms,不透明渲染均值4.8ms,耗时加起来超过10ms,该值稍高,现在渲染模块的峰值是12W(属于偏高的级别),在中低端机器上会造成一定的压力。
image007.png (43.17 KB, 下载次数: 37)
18:20 上传
在这里我们来看下半透明渲染和不透明渲染的耗时走势。不透明渲染的数值可以对应三角形面片,我们可以看到峰值处两者是比较一致的。一般来说,不透明物体的渲染优化需要简化场景中渲染面片量。
image009.png (176.1 KB, 下载次数: 40)
18:20 上传
同时,我们还能观察到,多数情况下半透明渲染的耗时和Draw Call的走势接近。
image011.png (167.15 KB, 下载次数: 39)
18:20 上传
对于半透明渲染优化,一般主要从两部分入手:粒子系统和UI模块,为此我们来看下粒子系统的数据。
image013.png (185.53 KB, 下载次数: 39)
18:20 上传
上文我们截取了半透明和不透明渲染的耗时走势,下面我们就将其和粒子系统的走势做个对比。大致看到,紫色半透明和粒子系统的渲染曲线在很大程度上是吻合的;同时,我们也可以看到半透明渲染的耗时曲线在后续都高于10ms,由于粒子系统主要集中在3-4ms,所以我们可以大致判断出,渲染的问题很大程度上不是粒子系统的问题,具体分析可以见以下视频。
由上推断,我们不妨来看下UI模块耗时的走势。下图中,黄色线是NGUI中UIPanel.LateUpdate的开销,大致可以理解为NGUI在做网格重建和更新时的开销。正常情况下应该是稳定的,但是出现重建的时候会有峰值,在这个项目中有时候接近10ms甚至20ms。
image015.png (207.21 KB, 下载次数: 36)
18:20 上传
一般来说,出现比较大的重建时,会对渲染模块的性能产生明显影响。所以通过UI模块和粒子模块的数据对比来看,我们基本上可以判定,渲染模块的开销主要集中在UI模块和粒子系统,相对来说,UI模块更多一点。针对这两个模块,从现在的数据来看主要优化UI模块,因为单从UI模块的数据上来看,6.1ms的CPU均值非常高,我们推荐控制在3ms。
另外需要研发团队考虑的一点是堆内存分配总值(143.8MB),主要是由NGUI网格重建所致,重建操作越频繁,该总值越高。
image017.png (39.58 KB, 下载次数: 37)
18:20 上传
从截图画面来看,UI偏向于静态的面板,HUD类似于飘动的字体、血条的使用频率应该没那么高,所以10~20ms的开销不是很合理,需要研发团队对复杂的面板进行检测,尽量保证这些大的面板中没有频繁变动的UI元素。
注意:在UWA对某些项目进行深度优化的时候,会经常看到技能的面板,因为出现冷却的遮罩或者数字,引起了整个UI Panel的重建,导致较高的UIPanel.LateUpdate的开销。我们建议大家将消失和出现的UI元素从复杂的面板中独立出来,从而将网格重建的范围减小,也会对半透明渲染提升性能。
渲染模块的细节部分还可以通过UWA报告中代码效率的Camera.Render来看。
image019.png (344.24 KB, 下载次数: 38)
18:20 上传
可以看到半透明渲染(Render.TransparentGeometry)占了40%,MeshRenderer.Render占了21%,拼合(ParticleSystem.RenderSingle)和没有拼合(ParticleSystem.RenderBatch)的加起来10%左右。所以半透明渲染这块,可以认为粒子系统占了10%,UI 和场景中半透明物件占了20%。就该项目的当前场景而言,其开销主要是UI界面造成的,所以,UI模块是当前半透明渲染的瓶颈,且具有比较大的优化空间。
Q:空中的视野会影响渲染模块的性能吗?
A:这里的渲染面片数不是指所有场景游戏中的模型之和,而是看到的模型面数。一般来说,空中视角会比战斗的视角范围大很多,一般战斗中不会看到非常大的模型,所以三角面片不会很高。空中战斗的话压力就比较高了。
Q:UI的网格重建怎么理解?
A:大家可以通过我们官方博客上的一些技术推文和直播回顾对UI优化有些大致的把握,了解UI 网格重建会影响到UIPanel.lateupdate等机制等概念。比如说一个很小的元素,我仅仅改个颜色,或者隐藏了,就会导致整个UI Panel的重建,这样就造成更高的耗时。
Q:我们的策划要求每个UI都会动画,这样优化是不是就很难?
A:这是有可能的,因为从数据上来看,现在还是有很多不合理的地方, 我们可以看到图中大量动态的UI元素还是比较少,我们猜测应该是某些UI 元素在频繁消失和出现,导致整个UI Panel在重建,这里就需要大家自行去定位哪些UI Panel有问题了。
3. 动画模块
从报告中看到大家同时用到Animator和Animation两个组件,前者耗时均值0.7 ms,后者耗时均值为1 ms,偶尔有些峰值,但大部分都在合理范围内。
image021.png (311.62 KB, 下载次数: 44)
18:20 上传
在动画模块的数据中,MeshSkinning的耗时有点偏高。比如上图这段区间内基本接近30 ms,看上去是和Boss战斗,推测是Boss的顶点数比较高导致,需要研发团队进一步确认。MeshSkinning的优化主要是通过降低顶点数的数量来优化。
动画模块的性能细节还能从UWA报告的代码效率查看,下图是Animation.Update的具体走势。
image023.png (99.09 KB, 下载次数: 38)
18:20 上传
我们可以看到,在多数情况下都是1ms左右,偶尔有峰值,点开该场景下的堆栈信息按钮,我们看到详细的堆栈分配情况:
image025.png (95.08 KB, 下载次数: 36)
18:20 上传
很明显,Animation.Update的耗时峰值主要由于Animation.RebuidInternalState 这个函数所致,该函数一般出现激活或实例化带有Animation组件的GameObject时,每次出现时开销都会比较高,避免的方法还是比较简单:我们现在看到大部分角色的缓存,我们都会禁用掉,但是如果开启禁用比较频繁,会导致这个函数经常出现。
一种比较好的优化方式是将怪物在隐藏或者放到缓冲池时,不是把GameOject的根节点禁用掉,而是把上面的Animation组件禁用掉(Enabled属性),激活时只需要开启Animation组件就可以,这样就可以避免这个函数的开销,从而降低这里的峰值。然后把一些逻辑等停掉,但是Animation根节点依然还在激活状态,不受影响。
Q:Animator也是有这个函数吗?
A:Animator.initialize和刚刚这个函数对应。这里的峰值也是因为带有Animator的组件做了SetActive(true)的操作,大家也可以后续做下检测。
在关注CPU的时候,我们也会关注GC调用的情况。
image027.png (46.25 KB, 下载次数: 38)
18:20 上传
GC的调用频率接近1000,目前看下来数据是比较合理的;另外GC的耗时在371ms,这个数值相对来说偏高。要优化CG耗时主要是通过两种方法:1)降低调用频率,即尽可能跑更多的帧再调用,2)降低GC每次的耗时。两种优化的方式不太一样,我们分别说明:
1)降低调用频率可以通过减少GC累积的分配数据来实现。我们可以跳转至代码效率-堆内存使用的页面。
image029.png (199.77 KB, 下载次数: 39)
18:20 上传
这些是堆内存累积的分配量,堆内存分配越少,则GC触发也越少。可以看到, 排名前两个函数的累积分配加起来有200MB左右,这里有大量的提升空间。第一个函数UIPanel.LateUpdate()是NGUI的函数,优化的方法并不是通过优化代码,而是尽可能优化NGUI网格重建的频率;第二个函数UERoot.Update()是大家的主逻辑,需要通过代码函数去定位堆内存的分配,现在我们已经有Mono堆内存测试功能,大家可以通过Mono报告去看具体函数的开销,定位起来相对起来会容易得多(后文将详细说明)。总而言之,GC调用频率的优化主要通过这个面板去找累积堆内存分配最高的函数,一点点去优化。
2)降低GC的耗时可以通过优化堆内存的峰值来实现。GC调用一次的开销和堆内存里的对象有关,即对象越多、峰值越高,则GC越高。该项目接近100MB的堆内存峰值是较高的,我们推荐降低到40MB范围以内。
堆内存的峰值一方面影响内存的大小、另一方面影响GC的CPU开销,需要大家特别注意。
其他模块的性能较为正常,在此不多做说明。
◆◆◆◆内存模块
在UWA报告的内存模块来看,在测试的十多分钟内,内存峰值达到378MB,堆内存峰值达到92.6MB,都是属于比较偏高的,下面我们将通过内存模块的几大构成来逐一分析。
image031.png (148.23 KB, 下载次数: 36)
18:20 上传
在堆内存的走势图中我们发现两个情况:首先是刚刚测试的时候就分配了68MB左右的堆内存,另一方面是堆内存的不断增长,最终达到了92MB。
image033.png (115.54 KB, 下载次数: 38)
18:20 上传
关于前者,多数是配置文件占用的空间比较大,或者缓存机制所致;针对后者,在这里建议大家可以参考UWA的Mono报告中高堆内存留存函数列表。如下图演示,通过点击右边的“蓝色箭头”,可以查看某些函数中生成的驻留在内存中的详细变量情况,从而能更快地判断和定位堆内存的泄露点。
image035.gif (1.14 MB, 下载次数: 43)
18:20 上传
2.&&资源内存—纹理
除了堆内存,我们再来看下资源内存。纹理的内存占用峰值为91MB,这个值在我们测试过的大量项目中看是属于偏高的。到底是哪些资源占了那么多的空间?我们可以跳转到UWA报告的具体资源信息中来各个击破。在这些资源的属性中,我们先来关注下数量峰值,如下图,我们可以看到不少资源的数量峰值出现了2、3等数值, 即相同的资源在测试包中出现了两份、三份。
image036.png (241.13 KB, 下载次数: 36)
18:20 上传
同时,我们发现五页左右的纹理都存在冗余两份的情况,其中不少是100KB的纹理资源的情况,所以造成了不少纹理资源的浪费,建议大家通过UWA资源检测工具来查看AssetBundle中是否有冗余,接下来检测代码,是否有反复加载和反复Unload导致纹理依然残留的情况。此外,从纹理资源的格式上来看是比较正常的,基本上用了各个平台支持的格式。
3. 资源内存—网格
网格的内存峰值在24MB,相对合理,但从走势上来看也会有持续向上的趋势,数量稍微偏高。同样,网格资源也存在一定的冗余问题,如下图所示。
image038.png (236.5 KB, 下载次数: 39)
18:20 上传
除了这个数量峰值,我们刚刚提到Color和Tangent属性,这两个属性对于大多数的Shader来说都用不上,所以需要研发团队进一步确认,是否有开启不必要的顶点属性。
Q:Tangent是否在NGUI中会被用到?
A:默认不会开启,最多生成normal。一般情况下是有color,但是没有normal和tangent。
4. 资源内存—动画资源
动画资源的数量也是比较多的,因此内存是明显偏高的。虽然存在一种可能,即如果大家采用了动画模块的缓存机制,的确会不断上涨,但建议研发团队也确认下这部分能否优化。毕竟报告中的数值相当高,足以引起大家重视了。
image040.png (184.27 KB, 下载次数: 42)
18:20 上传
Q:为什么资源会有冗余的情况?
A: AssetBundle之间本身有冗余,它们分别被加载进来后就会产生冗余;卸载后再加载,也会产生冗余。
Q:这个无法达到没有冗余的吧?
A:可以做到零冗余,主要控制好管理的机制。1、避免AB本身没有冗余;2、通过管理的方式提前知道这个纹理是否已经被加载等。在UWA博客中有几篇相关的技术文章,大家可以参考下。
5. 资源内存—Shader
Shader的内存一向都是比较小的,这里主要还是看数量峰值,因为Shader资源数量会造成Shader.Parse函数的开销。在UWA报告的“重要参数解析”一栏中,可以看到频繁出现了56-60ms的峰值,这些都是Shader的解析造成。因此Shader的冗余可以理解成Shader.Parse的使用频率更多了。检查冗余的办法和上述的一样。
image042.png (68.53 KB, 下载次数: 42)
18:20 上传
◆◆◆◆资源管理
在内存篇中,我们看到总体内存峰值为378MB,其中资源内存峰值将近227MB、堆内存的峰值92.6MB,那么剩余的将近60MB内存占用去哪儿了呢?这时,我们千万不能忽略这两大杀手:WebStream和SerializedFile。
1)WebStream内存占用
WebStream为Unity 5.3 以前版本的项目,通过特定API(new WWW、CreateFromMemory等)加载AssetBundle文件所开辟的较大块内存。主要用于存放AssetBundle的原始数据和解压后数据。
2)序列化信息内存占用
Unity引擎的序列化信息种类繁多,其中最为常见且内存占用较大的为 SerializedFile。在Unity 5.3之前的版本中,该序列化信息的内存分配主要为项目通过特定API(WWW.LoadFromCacheOrDownload、CreateFromFile等)加载AssetBundle文件所致。
对于这部分的优化,这就要结合UWA的另一项黑科技—资源管理。我们可以在该模块里看到加载资源时的总次数、加载方式、加载耗时等具体信息。
image044.png (108.15 KB, 下载次数: 36)
18:20 上传
从表中看,AssetBundle加载的频率是比较一致的,基本上都是十几次,并且有一定间隔,但是大家可以注意到有些AssetBundle重复加载的频率比较高,如下图中连续三次加载,这可能就是存在一些问题,比如缓存时间过短等等。
image046.gif (716.49 KB, 下载次数: 47)
18:20 上传
另外可以通过UWA报告中的具体AssetBundle使用情况模块查看AssetBundle在内存中的驻留情况,如下图所示。大家在Unity 4.x 版本上用的是WWW,所以内存中具有一定的WebStream的占用,加载的AssetBundle比较多,WebStream也会较大。目前来看,AssetBundle最高值13个,在合理范围之内。
image047.png (101.73 KB, 下载次数: 36)
18:20 上传
可以看到,资源加载主要是通过AB.load和AB.loadasync两个API,这个加载次数是比较合理的。
image049.png (24.69 KB, 下载次数: 35)
18:20 上传
同时,我们也能通过该面板查看到具体的资源加载和卸载的情况:
image051.png (378.37 KB, 下载次数: 39)
18:20 上传
资源实例化
做实例化操作的时候会有明显的开销,对于一些元素的SetActive的时候也会有开销。比如实例化的时候被操作了上百次,研发团队需要考虑是否在战斗中频繁出现,有这些元素存在的话,我们建议在关卡前做一次预加载,之后用到的时候通过缓冲池进行激活、禁用等等,来减少实例化的开销。
该项目SetActive操作比较高,总共有16万次的操作,我们看下频率较高的几个。
image053.png (90.55 KB, 下载次数: 41)
18:20 上传
我们看到Skillicon元素 (蓝色线)在战斗过程中有持续的SetActive的操作,由于我们是每十帧汇总一次数据,所以每帧就会有5 个的调用,所以大家要特别注意这些元素,如果只是个空的Object,那么SetActive的开销是非常小的,但是如果这个元素身上带了些组件,大家需要确认下这些组件身上是否存在一些每帧都先禁用然后通过某些条件再打开的的一些操作,导致一些问题。
所以从资源管理的面板中,我们可以看到AssetBundle加载、驻留、资源实例化和激活等情况,帮助我们把加载部分做得更流畅。一旦资源加载和实例化发生在战斗中,那么峰值基本上是难避免,现在我们看到相对比较合理的方法是:实例化操作在战斗刚刚开始时候发生,然后随着战斗的时间慢慢加长,后面的实例化时间应该尽可能避免掉,利用缓冲池的方法等去优化。
◆◆◆◆GPU性能
最后,我们看来下该游戏在三星S6上的GPU的耗时情况。
1. Overdraw
我们主要关注填充倍数均值,4.0x 我们可以认为在测试的过程中,平均每一帧的像素会被填充4次,该值较高,一般我们建议把这个数值控制在3.0x左右。
image055.png (40.63 KB, 下载次数: 37)
18:20 上传
虽然Overdraw总体问题不算大,但是我们也会通过曲线去找一下是否有比较高的地方。比方说某些区域的填充倍数会到18的情况,从对应的画面来看,可能是和BOSS战斗,或者进入一些区域的时候,看上去屏幕上的特效比较多,在峰值区域特效比较多,所以画面会比较亮,其实Overdraw较高在多数情况下就是因为半透明特效比较多、区域比较大所致。
image057.png (236.15 KB, 下载次数: 42)
18:20 上传
另外, UI界面展开的情况下也会比较大 。这种情况下如果游戏时间比较长,GPU的负载比较高,导致发热比较快。所以Overdraw比较高的地方需要大家关注,特别是持续时间比较长的界面,类似UI界面(因为UI都是半透的,所以Overdraw会叠起来)。对此,我们一般建议减少UI和其背后场景的重叠,比如下图中UI后面的场景还在正常进行的(后面的人物和背景都看得到),大家可以考虑下能否在全屏UI出现后把相机关掉,这样的话可以减少不必要的Overdraw开销。
image059.png (332.97 KB, 下载次数: 42)
18:20 上传
最后还有一些面积很大的特效,从截图上来看还好出现的时间比较短,所以优化的优先级略低。
以上就是该游戏的诊断内容,我们主要从性能的几大核心指标:CPU、内存、GPU三大模块反应的性能问题出发,通过数据报告的查看对比,整理出了一条较为完整的优化思路,希望能对大家的自身项目有所启发。也感谢该团队的分享,这也是鉴于我们相信这些数据的公开,能帮到更多游戏开发者省下优化的时间,将更多精力集中在游戏的开发和制作中去。}

我要回帖

更多关于 移动端性能优化问题 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信