日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【笔记】Unity优化 基础知识

發布時間:2024/3/26 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【笔记】Unity优化 基础知识 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

Find 和 FindObjectOfType

Camera.main?

按 ID 尋址

與 UnityEngine.Object 子類進行 Null 比較

矢量和四元數數學以及運算順序

使用非分配物理 API

Update 管理器

減少方法調用開銷

簡單屬性

簡單方法


Find 和 FindObjectOfType

一般來說,最好完全避免在生產代碼中使用?Object.Find?和?Object.FindObjectOfType

由于此類 API 要求 Unity 遍歷內存中的所有游戲對象和組件,因此它們會隨著項目規模的擴大而產生性能問題。

在單例對象的訪問器對上述規則來說是個例外。全局管理器對象往往會暴露“instance”屬性,并且通常在 getter 中存在?FindObjectOfType?調用以便檢測單例預先存在的實例。雖然這種模式通常是可以接受的,但必須注意檢查代碼并確保調用訪問器時場景中不存在單例對象。如果 getter 沒有自動創建缺失單例的實例,那么尋找單例的代碼經常會重復調用?FindObjectOfType(通常每幀多次發生)并且會對性能產生不良影響。

Camera.main?

如果代碼必須尋址 Main Camera,強烈建議使用下面的任意一種方式:

  • 在?Start()?或?OnEnable()中訪問??Camera.main,并緩存返回的這個引用。

  • 構造一個?Camera Manager?類,可提供或添加對活動攝像機對引用。

在內部,Unity 的?Camera.main?屬性會調用?Object.FindObjectWithTag(這是?Object.FindObject?的一個專用變體)。訪問此屬性并不比調用?Object.FindObjectOfType?更高效。

按 ID 尋址

每當在 Animator、Material 或 Shader 上使用?Set?或?Get?方法時,請使用整數值方法而非字符串值方法。

Unity 不使用字符串名稱對 Animator、Material 和 Shader 屬性進行內部尋址。為了加快速度,所有屬性名稱都經過哈希處理為屬性 ID,實際上正是這些 ID 用于尋址屬性。字符串方法只執行字符串哈希處理,然后將經過哈希處理的 ID 轉發給整數值方法。

從字符串哈希創建的屬性 ID 在單次運行過程中是不變的。它們最簡單的用法是為每個屬性名稱聲明一個靜態只讀整數變量,然后使用整數變量代替字符串。啟動期間將自動進行初始化,無需其他初始化代碼。

Animator.StringToHash?是用于 Animator 屬性名稱的對應 API,Shader.PropertyToID?是用于 Material 和 Shader 屬性名稱的對應 API。

與 UnityEngine.Object 子類進行 Null 比較

?對于從?UnityEngine.Object?派生的類的實例,與 Null 進行比較的成本雖然低,但遠高于與純 C# 類進行比較的成本。因此,請避免在緊湊循環中或每幀運行的代碼中進行此類 Null 比較。

Mono 和 IL2CPP 運行時以特定方式處理從?UnityEngine.Object?派生的類的實例。在實例上調用方法實際上是調用引擎代碼,此過程必須執行查找和驗證以便將腳本引用轉換為對原生代碼的引用。

矢量和四元數數學以及運算順序

運算由快到慢:int? > float > Vector

對于位于緊湊循環中的矢量和四元數運算,請記住整數(int)運算比浮點數(float)更快,而浮點數(float)矢量、矩陣或四元數(Vector2/3/4)運算更快。

因此,每當交換或關聯算術允許時,請嘗試最小化單個數學運算的成本:

Vector3 vector; int a; int b;Vector3 slow = a * vector * b; // 效率較低:產生兩次矢量乘法Vector3 fast = a * b * vector; // 效率較高:一次整數乘法、一次矢量乘法

使用非分配物理 API

?將?RaycastAll?調用替換為?RaycastNonAlloc,將?SphereCastAll?調用替換為?SphereCastNonAlloc,以此類推。

在 Unity 5.3 及更高版本中,引入了所有物理查詢 API 的非分配版本。對于 2D 應用程序,也存在所有 Physics2D 查詢 API 的非分配版本。

Update 管理器

在內部,Unity 會跟蹤感興趣的列表中的對象的回調(例如?Update、FixedUpdate?和?LateUpdate)。這些列表以侵入式鏈接列表的形式進行維護,從而確保在固定時間進行列表更新。在啟用或禁用 MonoBehaviour 時分別會在這些列表中添加/刪除 MonoBehaviour。

隨著回調數量的增加,這種方式將變得越來越低效。從原生代碼調用托管代碼回調有一個很小但很明顯的開銷。這會導致在調用大量每幀都執行的方法時延長幀時間,而且在實例化包含大量 MonoBehaviour 的預制件時延長實例化時間(注意: 實例化成本歸因于調用預制件中每個組件上的 Awake 和 OnEnable 回調時產生的性能開銷)。

當具有每幀回調的 MonoBehaviour 數量增長到數百或數千時,移除這些回調并將 MonoBehaviour(甚至標準 C# 對象)附加到一個global manager singleton單例可以優化性能。然后,global manager單例可將?Update、LateUpdate?和其他回調分發給感興趣的對象。這還有一個額外的好處,就是允許代碼在沒有操作的情況下巧妙地取消訂閱回調,從而減少了大量每幀必須調用的函數的數量。

性能上最大的節約來自于消除很少執行的回調。請考慮以下偽代碼:

void Update() {if(!someVeryRareCondition) { return; } // … 某些操作 … }

如果大量 MonoBehaviour 具有上述類似 Update 回調,則運行 Update 回調所使用的大量時間會用于原生和托管代碼域之間的切換以便執行 MonoBehaviour之后再立即退出。如果這些類僅在?someVeryRareCondition?==?true 時訂閱global Update Manager?,此后便退訂,則可節省代碼域切換和稀有條件評估所需的時間。

減少方法調用開銷

調用的每個方法都必須在內存中找到該方法的地址,并將另一個幀推入棧。所有這些操作都是有成本的,但在大多數代碼中,它們都小到可以忽略不計。

但是,在緊湊循環中運行較小的方法時,因引入額外方法調用而增加的開銷可能會變得非常顯著,甚至占主導地位。請考慮以下兩個簡單方法。

Example 1:

int Accum { get; set; } Accum = 0;for(int i = 0; i < myList.Count; i++) {Accum += myList[i]; }

Example 2:

int accum = 0; int len = myList.Count;for(int i = 0; i < len; i++) {accum += myList[i]; }

?

兩種方法都計算List中所有int之和。第一個示例是更“現代的 C#”,它使用自動生成的屬性來保存其數據值。

雖然從表面上看這兩段代碼似乎是等效的,但通過分析代碼中的方法調用情況,可看出差異很明顯。

示例 1:

int Accum { get; set; } Accum = 0;for(int i = 0;i < myList.Count; // 調用 List::getCounti++) {Accum // 調用 set_Accum += // 調用 get_Accum myList[i]; // 調用 List::get_Value }

每次循環執行時都有四個方法調用:

  • myList.Count?調用?Count?屬性上的?get?方法
  • Accum?屬性上的?get?和?set?方法肯定要調用
  • 通過?get?檢索?Accum?的當前值,以便將其傳遞給加法運算
  • 通過?set?將加法運算的結果分配給?Accum
  • []?運算符調用列表的?get_Value?方法來檢索列表特定索引位置的項值。

示例 2:

int accum = 0; int len = myList.Count;for(int i = 0;i < len; i++) {accum += myList[i]; // 調用 List::get_Value }

在第二個示例中,get_Value?調用仍然存在,但已刪除所有其他方法或不再是每個循環迭代便執行一次。

  • 由于?accum?現在是原始值而不是屬性,因此不需要進行方法調用來set或get其值。

  • 由于假設?myList.Count?在循環運行期間不變化,其訪問權限已移出循環的條件語句,因此不再在每次循環迭代開始時執行它。

這兩個版本的執行時間顯示了從這一特定代碼片段中減少 75% 方法調用開銷的真正優勢。在現代臺式機上運行 100,000 次的情況下:

  • 示例 1 需要的執行時間為 324 毫秒
  • 示例 2 需要的執行時間為 128 毫秒

簡單屬性

為了方便開發者,Unity 為數據類型提供了許多“簡單”常量。但是,鑒于上述情況,必須注意:這些常量通常是以返回常量值的「屬性」來實現的。

Vector3.zero 的屬性內容如下所示:

get { return new Vector3(0,0,0); }

Quaternion.identity 非常相似:

get { return new Quaternion(0,0,0,1); }

雖然訪問這些屬性的成本與它們周圍的執行代碼相比小的多,但它們每幀執行數千次(或更多次)時,可產生一定的影響。

對于簡單的原始類型,請改用?const?值。Const?值在編譯時內聯 - 對?const?變量的引用將替換為其值。

注意:因為對?const?變量的每個引用都替換為其值,所以不建議聲明長字符串或其他大型數據類型?const。否則,由于最終二進制代碼中的所有重復數據,將導致不必要地增加最終二進制文件的大小。

當?const?不適合時,應使用?static readonly?變量。在有些項目中,Unity 的內置簡單屬性即使替換成了?static readonly?變量,也會使性能略有改善。

簡單方法

簡單方法比較棘手。函數聲明一次然后就可以在其他地方重用它,絕對是很有用的。但是,在緊湊內部循環中,可能有必要打破美觀編碼規則,選擇“手動內聯”某些代碼。

有些方法可以徹底淘汰了,例如,Quaternion.Set、Transform.Translate?或?Vector3.Scale。這些方法執行非常簡單的操作,可以用簡單的賦值語句替換。

對于更復雜的方法,應權衡「手動內聯的性能提升」與「維護性能更佳的代碼的長期成本」之間的關系。

?


來源:

一般優化 - Unity 手冊有多少原因導致性能問題,就有多少種不同的方式來優化代碼。通常,強烈建議開發者在嘗試應用 CPU 優化之前對其應用程序進行性能分析。不過,還是存在幾種普遍適用的簡易 CPU 優化方式。https://docs.unity3d.com/cn/2019.4/Manual/BestPracticeUnderstandingPerformanceInUnity7.html

特別優化 - Unity 手冊?

總結

以上是生活随笔為你收集整理的【笔记】Unity优化 基础知识的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。