美文网首页
优化-CPU

优化-CPU

作者: 叫我颜先生 | 来源:发表于2018-06-15 17:44 被阅读0次

原文地址:https://blog.uwa4d.com/archives/optimzation_cpu.html
http://www.cnblogs.com/chwen/p/4396515.html

CPU方面的性能开销主要两大类:引擎模块和自身代码。其中,引擎模块中又可细致划分为渲染模块、动画模块、物理模块、UI模块、粒子系统、加载模块和GC调用等等。
主要就是drawcall、物理组件、GC(垃圾回收)、脚本等几个方面开展

渲染模块

  • 降低DrawCall
  • LOD
  • Occlusion Culling
1.DrawCall

Drawcall是CPU向GPU发送绘制命令的接口调用。理论上每一个不同材质的物件需要渲染在屏幕上时,CPU都会调用图形API ( openGL or Diract3D ) 的Draw接口触发显卡进行绘制。
如果每次drawcall只提交少量的数据将导致CPU瓶颈,CPU无法将GPU填满。Drawcall对GPU的耗费在于硬件一直等待CPU提交数据,而无法得到有效利用。GPU大量的时间耗费在不断切换状态和正确性检测上。 实际上unity官方指出,Drawcall数量的降低并非重点,重点是减少批次的数量,Drawcall优化实际上是对批次数量的优化

优化方案:Drawcall batching,合并打包图集,减少光照和阴影等

  • Drawcall Batching
    Unity中对Drawcall的批次有两种:静态批次(static batching)和动态批次(dynamic batching)。但不论静态批次还是动态批次都要求对象的材质是共享的,即不同材质的对象是无法进行批次的。而且要注意的一点:如果在脚本中调用材质时,使用Renderer.material会造成材质的拷贝,而使用Renderer.sharedMaterial来调用则不会拷贝材质。

    1. 静态批次 Drawcall static batching
      场景中的多个物件如果是不移动的(包括位置、缩放、旋转等),并且共享同一材质,比如地形、建筑、花盆等,那么可以选择采用静态批次。静态批次只需要在Inspector勾选static选项即可。静态批次需要注意的是,unity会将进行批次的多个对象合并成一个大的对象,也会导致内存损耗,有时候要避免太多对象静态批次造成的内存过高。这也表明,优化并非绝对做好某一方面,而是平衡各个硬件的瓶颈和效率,选择相对适中的方案

    2.动态批次 Drawcall dynamic batching
    动态批次是运动的物件在unity中也可以进行批次渲染,动态批次不需要手动设置,是unity自动进行的,但是这里有诸多陷阱和约束,开发者需要遵守一定的限制条件才能享受动态批次的好处。

  • 合并图集
    其实合并图集也是利用了Unity的Drawcall batching。将多个纹理进行打包成图集是为了减少材质,这样多个对象共享一个材质,并进而使用同一个纹理和shader,触发unity的动态批次。图集打包工具有很多,Asset store中也可以搜到不少,比如Texture Packer FreeDrawCall Optimizer(收费) Mesh Baker Free 等等都可以将贴图打包合并

  • 光照和阴影
    实时光照和阴影可能增加Drawcall,带有光源计算的shader材质会因为光照产生多个Drawcall。使用灯光会打断Drawcall batching,尽量使用烘焙灯光贴图等技巧来实现灯光效果。

2.LOD(https://blog.csdn.net/u014306293/article/details/72522834)

LOD技术指根据物体模型的节点在显示环境中所处的位置和重要度,决定物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算,就是根据与摄像机的距离来选择不同精密物体渲染

3.Occlusion Culling(https://blog.csdn.net/qq_33747722/article/details/70549006)

遮挡剔除、视锥剔除,这两个Unity提供的剔除方案,出视野之后应剔除对象渲染。

加载模块

仅次于渲染的第二大模块,主要包括资源加载,卸载,实例化,代码序列化
cpu开销在引擎中主要体现在Loading.UpdatePreloading和Loading.ReadObject两项中(Profiler)

  • Loading.UpdatePreloading:这一项仅在调用类似LoadLevel(Async)的接口处出现,主要负责卸载当前场景的资源,并且加载下一场景中的相关资源和序列化信息等。下一场景中,自身所拥有的GameObject和资源越多,其加载开销越大
  • Loading.ReadObject,记录的是资源加载时的真正资源读取性能开销,基本上引擎的主流资源(纹理资源、网格资源、动画片段等等)读取均是通过该项来进行体现,很大程度上决定了项目场景的切换效率。
1.纹理加载

纹理资源是项目加载过程中开销占用最大的资源之一,其加载效率由其自身大小决定。目前,决定纹理资源大小的因素主要有三种:分辨率、格式和Mipmap是否开启

2.网格加载

加载效率由自身大小决定

  • 资源的数据量对加载性能影响较大,面片数越多,其加载越为耗时。设备性能越差,其耗时差别越为明显
  • 关闭Read/Write功能会降低AssetBundle的物理大小,其降低量与资源本身数据量相关,略微提升加载效率,大幅度降低内存
3.Shader加载

一个Shader资源的物理Size仅几KB,在内存中也不过几十KB。所以,Shader资源的效率加载瓶颈并不在其自身大小的加载上,而是在Shader内容的解析上。几个小小的Shader,其加载耗时居然要高于几张Atlas纹理或者拥有上万片面的Mesh网格。

  • 通过依赖关系打包,将项目中的所有Shader抽离并打成一个独立的AssetBundle文件,其他AssetBundle与其建立依赖;
  • Shader的AssetBundle文件在游戏启动后即进行加载并常驻内存,因为一款项目的Shader种类数量一般在50~100不等,且每个均很小,即便全部常驻内存,其内存总占用量也不会超过2MB;
  • 后续Prefab加载和实例化后,Unity引擎会通过AssetBundle之间的依赖关系直接找到对应的Shader资源进行使用,而不会再进行加载和解析操作
4.场景加载
  1. 场景加载

    • 资源加载:资源加载几乎占据了整个加载过程的90%时间以上,其加载效率主要取决于资源的加载方式(Resource.Load或AssetBundle加载)、加载量(纹理、网格、材质等资源数据的大小)和资源格式(纹理格式、音频格式等)等等。不同的加载方式、不同的资源格式,其加载效率可谓千差万别
    • Instantiate实例化:在场景加载过程中,往往伴随着大量的Instantiate实例化操作,比如UI界面实例化、角色/怪物实例化、场景建筑实例化等等。在Instantiate实例化时,引擎底层会查看其相关的资源是否已经被加载,如果没有,则会先加载其相关资源,再进行实例化,这其实是大家遇到的大多数“Instantiate耗时问题”的根本原因,这也是为什么我们在之前的AssetBundle文章中所提倡的资源依赖关系打包并进行预加载,从而来缓解Instantiate实例化时的压力
  2. 场景卸载
    对于Unity引擎而言,场景卸载一般是由引擎自动完成的,即当我们调用类似Application.LoadLevel的API时,引擎即会开始对上一场景进行处理

    • Destory:引擎在切换场景时会收集未标识成“DontDestoryOnLoad”的GameObject及其Component,然后进行Destroy.同时,代码中的OnDestory被触发执行,这里的性能开销主要取决于OnDestroy回调函数中的代码逻辑
    • Resources.UnloadUnusedAssets:一般情况下,场景切换过程中,该API会被调用两次,一次为引擎在切换场景时自动调用,另一次则为用户手动调用(在场景加载后,用户调用它来确保上一场景的资源被卸载干净).该API的CPU开销主要集中在500ms~3000ms之间.其耗时开销主要取决于场景中Asset和Object的数量,数量越多,则耗时越慢

UI模块

  • NGUI
    • UIPanel.LateUpdate是NGUI开销最大的函数
    • UI重建以UIPanel为单位,尽量动态元素和静态元素分离到不同的UIPanel中,将因为动态UI元素引起的重建控制在较小范围内
    • 动态UI元素还可以再根据更新频率再次细分到不同的UIPanel中
    • 同一个UIPanel下的动态元素尽可能少,越多创建的Mesh越大,重建开销增加.比如,战斗中的血条可以做成多个UIPanel,每组下有5-10个动态血条为宜

物理模块

GC调用

GC是unity自动回收内存垃圾的回收器,这虽没有内存泄漏的风险,但是过多的垃圾回收会让CPU高负荷。这里就要避免不必要的内存申请和释放。可以在某个脚本定时清理垃圾,如void update(){if(Time.framecount %5 ==0)System.GC.collect();}
有以下几点需要注意:

  1. 字符串的拼接会产生临时字符串内存,移除代码中的字符串拼接,改用string.format,或stringbuilder,这没测。
  2. 用for代替foreach,foreach每次迭代产生24字节垃圾内存。100次循环就是2.4kB.
  3. 对象标签tag比较采用comparetag,不要用tag=="mytag"这样。
  4. 使用对象池。对象克隆也是调用new,因此对于可以循环利用的对象要采用对象池,比如子弹、特效、宝石等等。
  5. 尽量使用struct而非class,因为struct是栈区,class是堆区。

Unity官方给出的一些优化建议:

  1. PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU。
  2. 如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unlit目录下的。它们更高效。
  3. 尽可能共用材质。
  4. 将不需要移动的物体设为Static,让引擎可以进行其批处理。
  5. 尽可能不用灯光。
  6. 动态灯光更加不要了。
  7. 尝试用压缩贴图格式,或用16位代替32位。
  8. 如果不需要别用雾效(fog)
  9. 尝试用OcclusionCulling,在房间过道多遮挡物体多的场景非常有用。若不当反而会增加负担。
  10. 用天空盒去“褪去”远处的物体。
  11. shader中用贴图混合的方式去代替多重通道计算。
  12. shader中注意float/half/fixed的使用。
  13. shader中不要用复杂的计算pow,sin,cos,tan,log等。
  14. shader中越少Fragment越好。
  15. 注意是否有多余的动画脚本,模型自动导入到U3D会有动画脚本,大量的话会严重影响消耗CPU计算。
  16. 注意碰撞体的碰撞层,不必要的碰撞检测请舍去。

相关文章

  • Unity 3D 游戏优化

    Unity优化是一个很大的概念,我们优化时需要注意三个方面:CPU优化,GPU优化,内存优化. CPU方面的优化:...

  • iOS-面试题5-性能优化

    目录: 卡顿优化 耗电优化 启动优化 APP瘦身 一. 卡顿优化 CPU和GPU的作用CPU计算文字大小、位置、颜...

  • CPU 性能优化思路

    应用程序优化 编译器优化 算法优化 异步处理 多线程代替多进程 善用缓存 系统优化 cpu绑定 cpu独占 优先级...

  • 性能优化概述

    cpu 1、应用层 编译器优化, 算法优化, 异步处理, 善用缓存 2、底层 cpu绑定与cpu独占, 优先级调整...

  • 性能优化 ②

    CPU的方面的优化: CPU优化方向: 1、DrawCalls 2、物理组件(Physics) 3、GC(GC为处...

  • iOS底层原理之性能优化

    文章目录 CPU和GPU 卡顿优化 - CPU 卡顿优化 - GPU 离屏渲染 耗电 耗电优化 APP的启动 AP...

  • iOS底层原理--性能优化

    文章目录CPU和GPU 卡顿优化 - CPU 卡顿优化 - GPU 离屏渲染 耗电耗电优化 APP的启动 APP启...

  • iOS底层原理之性能优化

    文章目录CPU和GPU 卡顿优化 - CPU 卡顿优化 - GPU 离屏渲染 耗电耗电优化 APP的启动 APP启...

  • Mysql优化

    mysql优化 cpu指标 mem内存 swap 磁盘IO 优化的工具 主机选型 cpu选择 内存 磁盘选择 存储...

  • 性能优化02-布局优化

    性能优化02-布局优化 一、CPU与GPU 1、定义 为什么要了解CPU与GPU呢?因为布局绘制就是CPU与GPU...

网友评论

      本文标题:优化-CPU

      本文链接:https://www.haomeiwen.com/subject/odypeftx.html