Unity optimization related knowledge points

The following article is pasted from         Talking about Optimization: From Draw Calls to GC   

Commonly used optimization code related knowledge points, please go to the link to view the detailed optimization content


Processing memory:

Does it feel weird to talk about GC in the CPU part? In fact, Piff doesn't think so. Although GC is used to process memory, it does increase the CPU overhead. Therefore, it can indeed achieve the effect of releasing memory, but the cost is heavier, which will increase the burden on the CPU. Therefore, the optimization goal of GC is to trigger GC as little as possible.

First of all, we have to make it clear that the so-called GC is the mechanism of the Mono runtime, not the mechanism of the Unity3D game engine, so the GC is mainly for Mono objects, and it also manages Mono's managed heap. Figure this out, and you will understand that the GC is not used to deal with the memory release of the engine's assets (textures, sound effects, etc.), because the U3D engine also has its own memory heap instead of using the so-called managed heap with Mono .

Second, we need to figure out what will be allocated on the managed heap? Yes, it's a reference type. Such as instances of classes, strings, arrays, etc. And as int, float, including structure struct are actually value types, they will be allocated on the stack instead of the heap. So the objects we care about are nothing more than class instances, strings, and arrays.

So when will the GC be triggered? Two cases:

  1. First of all, of course, when our heap runs out of memory, GC will be called automatically.
  2. Secondly, as programmers, we can also manually call GC ourselves.

Therefore, in order to achieve the purpose of optimizing the CPU, we cannot trigger GC frequently. The above also said that GC deals with managed heaps, not those resources of the Unity3D engine, so GC optimization is simply code optimization. So Piff thinks there are a few things to keep in mind:

  1. Handling of string concatenation. Because the process of connecting two strings is actually the process of generating a new string. And the old string before it naturally becomes garbage. As a reference type string, its space is allocated on the heap, and the space of the discarded old string will be garbage collected by the GC.
  2. Try not to use foreach, use for instead. Foreach actually involves the use of iterators, and according to legend, the iterators generated by each loop will bring 24 Bytes of garbage. Then looping 10 times is 240Bytes.
  3. Do not directly access the tag property of the gameobject. For example, if (go.tag == "human") is best replaced with if (go.CompareTag ("human")). Because accessing the tag property of the object will allocate additional space on the heap. If this is done in the cycle, the garbage left behind can be imagined.
  4. Use "pools" for space reuse.
  5. It is best not to use LINQ commands, because they will allocate temporary space, which is also the target of GC collection. And one of the things I hate about LINQ is that it might not compile well for AOT in some cases. For example "OrderBy" will generate the internal generic class "OrderedEnumerable". This is not possible at AOT compile time because it is only used in the OrderBy method. So if you use OrderBy, you may get an error on the IOS platform.

Code and script:

When it comes to the topic of code, some people may think that Piff is superfluous. Because code quality varies from person to person, it is difficult to have a clear judging standard like the points mentioned above. Yes, it is reasonable for the public to write about the public, and it is reasonable for the mother to write about the mother. But the so-called code quality that PIF wants to mention here is based on a premise: Unity3D is written in C++, and our code is written in C# as a script, then the question arises~ Whether the interaction overhead between the script and the underlying layer is required Consider it? That is, we use Unity3D to write the game's "game scripting language", that is, C#, which is hosted by the mono runtime. The function is implemented by the C++ of the underlying engine, and the function implementation in the "game script" is inseparable from the call to the underlying code. So how should we optimize this part of the overhead?

  1. 以物体的Transform组件为例,我们应该只访问一次,之后就将它的引用保留,而非每次使用都去访问。这里有人做过一个小实验,就是对比通过方法GetComponent<Transform>()获取Transform组件, 通过MonoBehavor的transform属性去取,以及保留引用之后再去访问所需要的时间:
    • GetComponent = 619ms
    • Monobehaviour = 60ms
    • CachedMB = 8ms
    • Manual Cache = 3ms

   2.如上所述,最好不要频繁使用GetComponent,尤其是在循环中。

   3.善于使用OnBecameVisible()和OnBecameVisible(),来控制物体的update()函数的执行以减少开销。

   4.使用内建的数组,比如用Vector3.zero而不是new Vector(0, 0, 0);

   5.对于方法的参数的优化:善于使用ref关键字。值类型的参数,是通过将实参的值复制到形参,来实现按值传递到方法,也就是我们通常说的按值传递。复制嘛,总会让人感觉很笨重。比如Matrix4x4这样比较复杂的值类型,如果直接复制一份新的,反而不如将值类型的引用传递给方法作为参数。

好啦,CPU的部分匹夫觉得到此就介绍的差不多了。下面就简单聊聊其实匹夫并不是十分熟悉的部分,GPU的优化。


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325165336&siteId=291194637