{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/",
  "atlas": "https://www.pystone.net/?node=unity-csharp-cpu-performance-optimization#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization.md",
  "context": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:unity-csharp-cpu-performance-optimization",
  "slug": "unity-csharp-cpu-performance-optimization",
  "title": "【优化】Unity开发中的C#——CPU耗时优化",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [
    "Unity CSharp CPU 耗时优化"
  ],
  "summary": "【优化】Unity开发中的C ——CPU耗时优化",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "dd750f80ea57a95423c7716e2c57ba4e85ca648d546ebde54e16060ddb0bda43",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "【优化】Unity开发中的C#——CPU耗时优化",
      "anchor": "优化unity开发中的ccpu耗时优化",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E4%BC%98%E5%8C%96unity%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84ccpu%E8%80%97%E6%97%B6%E4%BC%98%E5%8C%96"
    },
    {
      "depth": 2,
      "text": "【优化】Unity开发中的C——CPU耗时优化",
      "anchor": "优化unity开发中的ccpu耗时优化-1",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E4%BC%98%E5%8C%96unity%E5%BC%80%E5%8F%91%E4%B8%AD%E7%9A%84ccpu%E8%80%97%E6%97%B6%E4%BC%98%E5%8C%96-1"
    },
    {
      "depth": 2,
      "text": "基本原理",
      "anchor": "基本原理",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86"
    },
    {
      "depth": 3,
      "text": "代码的编译运行",
      "anchor": "代码的编译运行",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E4%BB%A3%E7%A0%81%E7%9A%84%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8C"
    },
    {
      "depth": 3,
      "text": "优化的出发点",
      "anchor": "优化的出发点",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E4%BC%98%E5%8C%96%E7%9A%84%E5%87%BA%E5%8F%91%E7%82%B9"
    },
    {
      "depth": 4,
      "text": "CPU耗时",
      "anchor": "cpu耗时",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#cpu%E8%80%97%E6%97%B6"
    },
    {
      "depth": 4,
      "text": "内存分配（Memory Allocation）与GC优化",
      "anchor": "内存分配memory-allocation与gc优化",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8Dmemory-allocation%E4%B8%8Egc%E4%BC%98%E5%8C%96"
    },
    {
      "depth": 2,
      "text": "优化原则与经验",
      "anchor": "优化原则与经验",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E4%BC%98%E5%8C%96%E5%8E%9F%E5%88%99%E4%B8%8E%E7%BB%8F%E9%AA%8C"
    },
    {
      "depth": 3,
      "text": "Move code out of loops when possible",
      "anchor": "move-code-out-of-loops-when-possible",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#move-code-out-of-loops-when-possible"
    },
    {
      "depth": 3,
      "text": "Only run code when things change",
      "anchor": "only-run-code-when-things-change",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#only-run-code-when-things-change"
    },
    {
      "depth": 3,
      "text": "隔帧调用（Run code every [x] frames）",
      "anchor": "隔帧调用run-code-every-x-frames",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E9%9A%94%E5%B8%A7%E8%B0%83%E7%94%A8run-code-every-x-frames"
    },
    {
      "depth": 3,
      "text": "Use caching",
      "anchor": "use-caching",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#use-caching"
    },
    {
      "depth": 3,
      "text": "Use the right data structure",
      "anchor": "use-the-right-data-structure",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#use-the-right-data-structure"
    },
    {
      "depth": 3,
      "text": "Minimize the impact of garbage collection",
      "anchor": "minimize-the-impact-of-garbage-collection",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#minimize-the-impact-of-garbage-collection"
    },
    {
      "depth": 3,
      "text": "Use object pooling",
      "anchor": "use-object-pooling",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#use-object-pooling"
    },
    {
      "depth": 3,
      "text": "避免使用反射",
      "anchor": "避免使用反射",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E9%81%BF%E5%85%8D%E4%BD%BF%E7%94%A8%E5%8F%8D%E5%B0%84"
    },
    {
      "depth": 4,
      "text": "反射性能差的原因",
      "anchor": "反射性能差的原因",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E5%8F%8D%E5%B0%84%E6%80%A7%E8%83%BD%E5%B7%AE%E7%9A%84%E5%8E%9F%E5%9B%A0"
    },
    {
      "depth": 3,
      "text": "Avoiding expensive calls to the Unity API",
      "anchor": "avoiding-expensive-calls-to-the-unity-api",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#avoiding-expensive-calls-to-the-unity-api"
    },
    {
      "depth": 4,
      "text": "SendMessage()",
      "anchor": "sendmessage",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#sendmessage"
    },
    {
      "depth": 4,
      "text": "Find()",
      "anchor": "find",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#find"
    },
    {
      "depth": 4,
      "text": "Transform",
      "anchor": "transform",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#transform"
    },
    {
      "depth": 4,
      "text": "Update()",
      "anchor": "update",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#update"
    },
    {
      "depth": 4,
      "text": "Vector2 and Vector3",
      "anchor": "vector2-and-vector3",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#vector2-and-vector3"
    },
    {
      "depth": 4,
      "text": "Camera.main",
      "anchor": "cameramain",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#cameramain"
    },
    {
      "depth": 4,
      "text": "ComputeBuffer.GetData()",
      "anchor": "computebuffergetdata",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#computebuffergetdata"
    },
    {
      "depth": 4,
      "text": "SetPixels()/GetPixels",
      "anchor": "setpixelsgetpixels",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#setpixelsgetpixels"
    },
    {
      "depth": 4,
      "text": "GetComponentsInChildren",
      "anchor": "getcomponentsinchildren",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#getcomponentsinchildren"
    },
    {
      "depth": 4,
      "text": ".material & .materials",
      "anchor": "material-materials",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#material-materials"
    },
    {
      "depth": 3,
      "text": "Running code only when it needs to run",
      "anchor": "running-code-only-when-it-needs-to-run",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#running-code-only-when-it-needs-to-run"
    },
    {
      "depth": 4,
      "text": "利用Culling的原理",
      "anchor": "利用culling的原理",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E5%88%A9%E7%94%A8culling%E7%9A%84%E5%8E%9F%E7%90%86"
    },
    {
      "depth": 4,
      "text": "利用LOD的原理",
      "anchor": "利用lod的原理",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#%E5%88%A9%E7%94%A8lod%E7%9A%84%E5%8E%9F%E7%90%86"
    },
    {
      "depth": 2,
      "text": "Ref",
      "anchor": "ref",
      "citation": "https://www.pystone.net/notes/unity-csharp-cpu-performance-optimization/#ref"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:performance-optimization-cost-frequency-scope",
      "title": "性能优化的本质是控制成本发生的频率、时机与范围",
      "url": "https://www.pystone.net/notes/performance-optimization-cost-frequency-scope/",
      "atlas": "https://www.pystone.net/?node=performance-optimization-cost-frequency-scope#knowledge-atlas",
      "label": "性能优化的本质是控制成本发生的频率、时机与范围",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "CPU 耗时优化强调减少重复调用、只在状态变化时执行、缓存结果和控制高频引擎 API；内存与 GC 优化则说明频繁堆分配最终会转化为 GC 扫描、停顿与堆扩张成本。",
      "citation": "https://www.pystone.net/notes/performance-optimization-cost-frequency-scope/#cpu-%E4%B8%8E%E5%86%85%E5%AD%98%E5%B9%B6%E4%B8%8D%E6%98%AF%E4%B8%A4%E6%9D%A1%E7%8B%AC%E7%AB%8B%E4%BC%98%E5%8C%96%E7%BA%BF"
    },
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "游戏性能优化中的“【优化】Unity开发中的C#——CPU耗时优化”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#%E6%B8%B8%E6%88%8F%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96"
    }
  ],
  "contentMarkdown": "\n# 【优化】Unity开发中的C#——CPU耗时优化\n\n> 创建时间：2021/1/25 14:27\n\n## 【优化】Unity开发中的C——CPU耗时优化\n  * 【优化】Unity开发中的C#——CPU耗时优化\n    * 基本原理\n      * 代码的编译运行\n      * 优化的出发点\n        * CPU耗时\n        * 内存分配（Memory Allocation）与GC优化\n    * 优化原则与经验\n      * Move code out of loops when possible\n      * Only run code when things change\n      * 隔帧调用（Run code every [x] frames）\n      * Use caching\n      * Use the right data structure\n      * Minimize the impact of garbage collection\n      * Use object pooling\n      * 避免使用反射\n        * 反射性能差的原因\n      * Avoiding expensive calls to the Unity API\n        * SendMessage()\n        * Find()\n        * Transform\n        * Update()\n        * Vector2 and Vector3\n        * Camera.main\n        * ComputeBuffer.GetData()\n        * SetPixels()/GetPixels\n        * GetComponentsInChildren\n        * .material & .materials\n      * Running code only when it needs to run\n        * 利用Culling的原理\n        * 利用LOD的原理\n    * Ref\n\n## 基本原理\n\nWhen our game runs, the central processing unit (CPU) of our device carries out instructions. Every single frame of our game requires many millions of these CPU instructions to be carried out. To maintain a smooth frame rate, the CPU must carry out its instructions within a set amount of time. When the CPU cannot carry out all of its instructions in time, our game may **slow down, stutter or freeze**.\n\n> 当出现低帧率（low frame rates）、性能不稳定（jerky performance） 或者间歇性的卡顿（intermittent freezes）等现象时，可以先判断性能问题是否由于CPU无法及时完成它的任务所造成。\n\n### 代码的编译运行\n\nCPUs can only run code written in very simple languages known as machine code or native code.\n（1）Unity first compiles our scripts into a language called Common Intermediate Language (CIL). CIL is a language that is easy to compile into a wide range of different native code languages.\n（2）The CIL is then compiled to native code for our specific target device.\n\nThis second step happens either when we build our game (known as ahead of time compilation or **AOT** compilation), or on the target device itself, just before the code is run (known as just in time compilation or **JIT** compilation). Whether our game uses AOT or JIT compilation usually depends on the target hardware.\n\nThe source code that we write determines the structure and content of the compiled code.For the most part, source code that is well structured and efficient will result in compiled code that is well structured and efficient.\n\n  * Some CPU instructions take more time to execute than others. (Calculating a square root takes a CPU more time to execute than, multiplying two numbers. )\n\n  * Some operations that seem very simple in source code can be surprisingly complex when they are compiled to code.\n\n### 优化的出发点\n\n#### CPU耗时\n\n游戏运行的每一帧，CPU需要执行大量的指令，如果每帧CPU指令耗时过高，即CPU无法及时地完成它在一帧中的任务，就会带来帧率下降、卡顿等现象。\n\n一些造成不必要的耗时的情形：\n\n  * makes the same function call repeatedly when it could make the call only once.\n\n  * unnecessary calls between managed and engine code.\n\n  * our code is efficient but it is being called when it does not need to be.\n\n> Eg. : simulates an enemy’s line of sight. The code itself may perform well, but it is wasteful to run this code when the player is very far from the enemy.\n\n> （Communication between Unity Engine code and our script code）C#与C++之间的数据传递会造成性能开销的原因：\n>  Most of the core functionality of the Unity Engine is written in C++ and has already been compiled to native code. This compiled engine code is part of what we install when we install Unity. Code compiled to CIL, such as our source code, is known as managed code. When managed code is compiled to native code, it is integrated with something called the managed runtime. The managed runtime takes care of things like automatic memory management and safety checks to ensure that a bug in our code will result in an exception rather than the device crashing. When the CPU transitions between running engine code and managed code, work must be done to set up these safety checks. When passing data from managed code back to the engine code, the CPU may need to do work to convert the data from the format used by the managed runtime to the format needed by the engine code. This conversion is known as marshaling. The overhead from any single call between managed and engine code is not particularly expensive, but it is important that we understand that this cost exists.\n\n#### 内存分配（Memory Allocation）与GC优化\n\nC#程序开发要遵循的一个基本原则就是避免不必要的堆内存的分配，而堆内存分配主要会造成以下后果：\n\n  * 程序所占用的内存总量过大。内存是有限的资源，对于游戏（特别是移动游戏）来说，内存的占用可谓是寸土寸金的。无法释放的内存过多会直接造成程序崩溃，游戏无法运行。\n\n  * 过多的分配次数会导致堆碎片变多。碎片过多可能导致无法开辟出所需要的连续的内存，这也会导致程序崩溃。\n\n  * 内存分配会触发GC（垃圾回收），而GC的代价较高，会造成卡顿。对GC的优化，本质上也是对CPU耗时的优化。\n\n## 优化原则与经验\n\n### Move code out of loops when possible\n\nInefficiencies can really add up if they are in a loop that runs very frequently.\n\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n\n### Only run code when things change\n\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n\n### 隔帧调用（Run code every [x] frames）\n\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n\n### Use caching\n\nIf our code repeatedly calls expensive functions that return a result and then discards those results, this may be an opportunity for optimization. Storing and reusing references to these results can be more efficient. This technique is known as caching.\n\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n\n### Use the right data structure\n\nTo make the right decision about which data structure to use, we need to understand the strengths and weaknesses of different data structures and think carefully about what we want our code to do.\n\n### Minimize the impact of garbage collection\n\n对GC的优化，本质上是对CPU耗时的优化，避免GC带来的卡顿。该部分内容将在内存优化部分进行讲解。\n\n### Use object pooling\n\nIt’s usually more costly to instantiate and destroy an object than it is to deactivate and reactivate it.\n\nObject pooling is a technique where, instead of creating and destroying instances of an object, objects are temporarily deactivated and then recycled and reactivated as needed.\n\n### 避免使用反射\n\nReflection is the term for when code examines and makes decisions about itself at run time rather than at compile time. Code that uses reflection results in far more work for the CPU than code that does not use reflection.\n反射是一项很基础的技术，它将编译期间的静态绑定转换为延迟到运行期间的动态绑定。在很多场景下（特别是类框架的设计），可以获得灵活易于扩展的架构。但带来的问题是与静态绑定相比，动态绑定会对性能造成较大的伤害。\n\n#### 反射性能差的原因\n\n在设计的时候反射本身就不是以高性能为目标的。\n在使用反射时，要获取较多的方法信息、进行参数校验和错误处理、安全性检查等等，耗时较多，并且很可能会造成堆内存分配。\n\n### Avoiding expensive calls to the Unity API\n\n#### SendMessage()\n\nSendMessage() and BroadcastMessage() are very flexible functions that require little knowledge of how a project is structured and are very quick to implement. As such, these functions are very useful for prototyping or for beginner-level scripting.\n\n**However, they are extremely expensive to use. This is because these functions make use of reflection.**\n\nSendMessage, similarly, will sweep through all of the attached Components, but will also sweep through all methods in all of those Components, looking for methods whose name matches the string provided as the first argument of the SendMessage method.\n\n> 应当如何做？\n>  For example, if we know which component we want to call a function on, we should reference the component directly and call the function that way. If we do not know which component we wish to call a function on, we could consider using Events or Delegates.\n\n#### Find()\n\nFind() and related functions are powerful but expensive. These functions require Unity to iterate over every GameObject and Component in memory. This means that they are not particularly demanding in small, simple projects but become more expensive to use as the complexity of a project grows.It’s best to **use Find() and similar functions infrequently and to cache the results where possible**.\n\n#### Transform\n\n  1. **Setting the position or rotation** of a transform causes an internal OnTransformChanged event to propagate to all of that transform’s children. This means that it’s relatively expensive to set a transform’s position and rotation values, especially in transforms that have many children.\n\n  2. **Transform.position** is an example of an accessor that results in a calculation behind the scenes. This can be contrasted with Transform.localPosition. The value of localPosition is stored in the transform and calling Transform.localPosition simply returns this value. However, the transform’s world position is calculated every time we call Transform.position.If our code makes frequent use of Transform.position and we can use Transform.localPosition in its place, this will result in fewer CPU instructions and may ultimately benefit performance. If we make frequent use Transform.position, we should cache it where possible.\n\n#### Update()\n\nUpdate(), LateUpdate(), FixUpdate() and so on have a hidden overhead.\n\n  * These functions require **communication between engine code and managed code** every time they are called.\n\n  * Unity carries out a number of safety checks before calling these functions. The safety checks ensure that the GameObject is in a valid state, hasn’t been destroyed, and so on.\n\nBehind the scenes, these safety checks and native calls still happen even when the body of the Update() function is blank.\n\n> This overhead is not particularly large for any single call, but it can add up in a game that has thousands of MonoBehaviours.For this reason, empty Update() calls can be particularly wasteful.\n\n#### Vector2 and Vector3\n\nVector math operations are simply more complex than float or int math operations.\nCPU instructions required to perform a square root calculation are slower than those used for, say, simple multiplication. Both Vector2.magnitude and Vector3.magnitude are examples of this, as they both involve square root calculations. Additionally, Vector2.Distance and Vector3.Distance use magnitude behind the scenes.If our game makes extensive and very frequent use of magnitude or Distance, it may be possible for us to avoid the relatively expensive square root calculation by using Vector2.sqrMagnitude and Vector3.sqrMagnitude instead.\n\n**Replacing a single call will result in only a tiny difference, but at a sufficiently large scale, it may be possible to make a useful performance saving.**\n\n#### Camera.main\n\nIt looks like a variable but is in fact an accessory.\nThe accessor calls an internal function similar to Find() behind the scenes.\nAll GameObjects with tags were previously searched, and any GameObjects with a matching tag were pulled out into a temporary array. Then that second list would be searched, and if any object had an enabled camera component, it was returned.\nIt searches through all GameObjects and Components in memory and can be very expensive to use.\nTo avoid this potentially expensive call, we should either cache the result of Camera.main or avoid its use altogether and manually manage references to our cameras.\n\n2020.2后的版本有优化，可以忽略这一条：<https://blogs.unity3d.com/cn/2020/09/21/new-performance-improvements-in-unity-2020-2/>\n\n#### ComputeBuffer.GetData()\n\nsynchronous operation\nComputeBuffer.GetData() stalls the CPU until the GPU has finished accessing the buffer. This can lead to significant CPU performance problems.\n\n#### SetPixels()/GetPixels\n\nUnity is using two distinct representations for a RGBA Color:\n\nColor: Each color component is a floating point value with a range from 0 to 1. (this format is used inside all graphics cards and shaders).\nColor32: Each color component is a byte value with a range from 0 to 255. (32bit RGBA).\n\nColor32 is much faster and uses 4X less memory. Color and Color32 can be implicitly converted to each other.\n\nCompared to SetPixels/GetPixels, SetPixels32/GetPixels32 is much faster and uses less memory.\n\nMoreover, GetPixels causes Mono memory allocation. You are not suggested to call it in frequently\n\n#### GetComponentsInChildren\n\n  * CPU processing overhead\nThe more child objects and components there are, the slower the method gets.\n\n  * MemoryAllocation\nSome versions of GameObject.GetComponentsInChildren() allocate managed memory.\n\nIf you call it in Update() method, it will keep on allocating memory\n\nEnsure you are using one of the versions of GameObject.GetComponentsInChildren() which accepts a `List<T>` as a parameter and populates it with the components it finds.\n\n#### .material & .materials\n\n对Renderer类型调用.material和.materials，会生成新的材质球实例。其影响及解决方案主要如下：\n\n**影响：**\n\n  1. 每次对新的GameObject的Renderer调用.material，都会生成一个新的Material实例，且GameObject销毁后，Material实例无法自动销毁，这对会资源管理造成一定的成本。需要手动调用“OnloadUnusedAssets”来卸载，造成性能开销；管理不好可能会造成材质球大量冗余甚至泄露，极端情况下会导致高内存。\n\n  2. 通过.material，创建材质实例，并修改属性的方式，时间开销较高。\n\n  3. 使用相同Shader，但Material实例不同的GameObject，无法进行合批，导致DrawCall增加，变相造成CPU耗时。\n\n**解决方法一:**\n使用MaterialPropertyBlock。\n\n  * 避免了新的材质球的创建，避免了材质球的冗余问题。\n\n  * 该方法修改材质属性，节约了拷贝拷贝材质的过程，耗时较低。\n\n> 该方法无法解决合批问题\n\n**解决方法二:**\n不要每次都.material。在代码中人为new处Material的实例，对有限个显示效果使用有限个Material实例。比如要实现一个Shader的三种效果，可以手动创建三个材质球实例，显示相同效果的物体，可以共用一个实例。\n\n  * 避免材质球冗余\n\n  * 能够精准地卸载\n\n  * 使用相同示例的物体可以合批。\n\n### Running code only when it needs to run\n\n#### 利用Culling的原理\n\nUnity contains code that checks whether objects are within the frustum of a camera. If they are not within the frustum of a camera, code related to rendering these objects does not run. The term for this is **frustum culling**.\n\n示例如下 - 利用可见性决定是否执行UpdateAnimations\n\n```csharp\nstring s1 = &quot;Hello &quot;;\nstring s2 = s1;\ns1 += &quot;World&quot;;\n\nSystem.Console.WriteLine(s2);\n//Output: Hello\n\n```\n\n#### 利用LOD的原理\n\nWe could use a level of detail system to enable and disable these expensive operations based on the enemy’s distance from the player.\n\nUnity’s **CullingGroup** API allows us to hook into Unity’s LOD system to optimize our **code**.\n\n## Ref\n\n<https://www.cnblogs.com/heroxiaohuihui/p/8057365.html>\n<https://learn.unity.com/tutorial/fixing-performance-problems-2019-3>\n\n<https://zhuanlan.zhihu.com/p/105292546>\n\n<https://github.com/microsoft/Microsoft.Unity.Analyzers/blob/main/doc/UNT0017.md>\n"
}
