Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

最佳化

效能

我們必須知道“瓶頸”在哪裡,才能知道如何加快我們的程式。瓶頸是指程式中最慢的部分,限制了所有事情的進展速度。專注於瓶頸,可以讓我們集中精力優化能給我們帶來最大速度提升的地方,而不是花大量時間去優化那些能帶來微小性能提升的功能。

對於 CPU 來說,找出瓶頸的最簡單方法就是使用性能剖析器。

分析工具

剖析器與您的程式一起運作, 並進行時間測量, 以計算出每個功能所花費的時間比例.

Godot整合式開發環境有一個方便的內建剖析器. 它不會在每次啟動專案時運作: 必須手動啟動和停止. 這是因為, 與大多數剖析器一樣, 記錄這些時序測量會大大減慢你的專案速度.

剖析後, 你可以回看一影格的結果.

使用現有的 Godot 材質

其中一個演示專案的簡介結果.

備註

我們可以看到物理, 音訊等內建流程的消耗, 也可以在底部看到自己腳本功能的消耗.

等待各種內建伺服器的時間可能不會被計算在剖析器中. 這是一個已知的錯誤.

當一個專案運作緩慢時, 你經常會看到一個明顯的功能或流程比其他功能或流程花費更多的時間. 這是你的主要瓶頸, 你通常可以通過優化這個領域來提高速度.

更多有關光照烘焙的資訊,請參考 doc_baked_lightmaps

External Files - 外部檔案

雖然Godot IDE剖析器非常方便有用, 但有時你需要更強大的功能, 以及對Godot引擎原始程式碼本身進行剖析的能力.

你可以 使用若干個協力廠商 C++ 分析器 來實作。

Callgrind 的截圖

例子結果來自Callgrind, 這是Valgrind的一部分.

從左邊開始,Callgrind正在列出函式及其子函式內的時間百分比(Inclusive), 函式本身(不包括子函式)內的時間百分比(Self), 函式被呼叫的次數, 函式名稱以及檔或模組.

在這個例子中,我們可以看到幾乎所有的時間都花在 Main::iter() 函式下。這是 Godot 原始程式碼中被反復呼叫的主函式。它導致影格被繪製、物理週期被類比、節點和腳本被更新。很大一部分時間是花在渲染畫布的函式中(66%),因為這個例子使用的是 2D 基準。下面,我們看到幾乎 50% 的時間都花在了 Godot 程式碼之外的 libglapi``i965_dri``(圖形驅動)中。這告訴我們,很大一部分 CPU 時間都花在了圖形驅動上。

這其實是一個很好的例子, 因為在理想的世界裡, 只有很小一部分時間會花在圖形驅動上. 這說明存在一個問題, 就是在圖形API中進行了太多的交流和工作. 這種特殊的剖析導致了2D批次處理的發展, 通過減少這方面的瓶頸, 大大加快了2D渲染的速度.

數學函式

另一個方便的技術, 特別是當你使用分析器確定了瓶頸後, 就是手動為功能或被測區域計時. 具體細節因語言而異, 但在GDScript中, 你可以做如下操作:

var time_start = OS.get_ticks_usec()

# Your function you want to time
update_enemies()

var time_end = OS.get_ticks_usec()
print("update_enemies() took %d microseconds" % time_end - time_start)

當手動為函式計時時, 通常最好是多次(1000次或更多次)運作該函式, 而不是只運作一次(除非是非常慢的函式). 這樣做的原因是, 計時器的精度往往有限. 此外,CPU會以一種無序的方式調度程序. 因此, 一系列運作的平均值比單次測量更準確.

當你嘗試優化功能時, 一定要反復對它們進行剖析或計時. 這將為您提供關鍵的回饋, 說明優化是否有效(或無效).

快取

CPU快取是另外一個需要特別注意的東西, 特別是在比較一個函式的兩個不同版本的時序結果時. 其結果可能高度依賴於資料是否在CPU快取中.CPU不會直接從系統RAM中載入資料, 儘管它與CPU快取相比非常巨大(幾千百萬位元組而不是幾百萬位元組). 這是因為系統RAM的存取速度非常慢. 相反,CPU從一個較小, 較快的記憶體庫中載入資料, 稱為cache. 從快取中載入資料的速度非常快, 但每次你試圖載入一個沒有儲存在快取中的記憶體位址時, 快取必須前往主記憶體並緩慢地載入一些資料. 這種延遲會導致CPU長時間閒置, 被稱為 "cache miss".

這意味著, 第一次運作一個函式時, 由於資料不在CPU快取中, 它可能運作得很慢. 第二次和以後的時間, 可能運作得更快, 因為資料在快取中. 由於這個原因, 在計時時一定要使用平均數, 並且要注意快取的影響.

瞭解快取對於CPU優化也是至關重要的. 如果你有一個演算法(常式), 從主記憶體隨機分佈的區域載入小資料位元, 這可能會導致大量的快取失誤, 很多時候,CPU會在附近等待資料, 而不是做別的工作. 相反, 如果你能使你的資料存取當地語系化, 或者更好的是以線性方式存取記憶體(像一個連續的列表), 那麼快取將以最佳方式工作,CPU將能夠盡可能快地工作.

Godot通常會為你處理這些低級的細節. 例如, 伺服器API確保資料已經為渲染和物理學等方面的快取進行了優化. 不過, 在使用 GDNative 時, 你還是要特別注意快取問題.

語言

Godot支援多種不同的語言, 值得注意的是, 其中有一些折衷. 有些語言是以速度為代價而設計的, 便於使用, 而另一些語言速度更快, 但更難使用.

無論你選擇哪種指令碼語言, 內建的引擎函式都以同樣的速度運作. 如果你的專案在自己的程式碼中進行了大量的計算, 可以考慮將這些計算轉移到更快的語言中.

GDScript

GDScript 被設計成易於使用和反覆運算的語言, 是製作多種型別遊戲的理想選擇. 然而, 在這種語言中, 易用性被認為比性能更重要. 如果您需要進行繁重的計算, 請考慮將您的一些專案轉移到其他語言中.

C#

C # 很受歡迎, 在Godot中得到了一流的支援. 它在速度和易用性之間提供了一個很好的折中. 不過要注意遊戲過程中可能出現的垃圾收集暫停和洩漏. 解決垃圾收集問題的一個常見方法是使用 物件集區, 這不在本指南的範圍內.

設計語言

協力廠商提供對其他幾種語言的支援,包括 RustJavascript

C++

Godot是用C++編寫的. 使用C++通常會帶來最快的程式碼. 然而, 在實際操作層面上, 它是最難在不同平臺上部署到終端使用者的機器上的. 使用C++的選項包括 GDNativecustom modules .

執行緒

在進行大量的計算時, 考慮使用執行緒, 這些計算可以相互並行運作. 現代CPU有多個核心, 每個核心能做的工作量有限. 通過將工作分散在多個執行緒上, 你可以進一步向CPU的峰值效率邁進.

執行緒的缺點是,你必須非常小心。由於每個 CPU 核心都是獨立運作的,它們最終可能會在同一時間試圖存取相同的記憶體。一個執行緒可以在另一個執行緒在寫的時候讀取一個變數:這被稱為*競態條件*。在你使用執行緒之前,請確保你瞭解這些危險以及如何嘗試和防止這些競態條件。

更多有關光照烘焙的資訊,請參考 doc_baked_lightmaps

SceneTree

雖然節點是一個非常強大和通用的概念, 但請注意, 每個節點都是有代價的. 內建的函式, 如 _process()_physics_process() 會在樹中傳播. 當你有非常多的節點(通常是成千上萬的節點)時, 這種內務管理會降低性能.

在Godot渲染器中, 每個節點都是單獨處理的. 因此, 較少的節點數量與較多的每個節點可以帶來更好的性能.

SceneTree 的一個怪癖是, 你有時可以通過從SceneTree中刪除節點, 而不是通過暫停或隱藏節點來獲得更好的性能. 您不一定要刪除一個分離的節點. 例如, 您可以保留一個節點的引用, 使用 Node.remove_child(node) 將其從場景樹中分離出來, 然後使用 Node.add_child(node) 將其重新連接. 例如, 這對於在遊戲中新增和刪除區域是非常有用的.

你可以通過使用伺服器API來完全避免使用SceneTree. 更多資訊, 請參見 使用伺服器進行優化 .

物理

在某些情況下, 物理學最終會成為一個瓶頸. 尤其是在複雜的世界和大量物理物件的情況下, 更是如此.

有幾點需要注意:

  • 嘗試使用簡化版本的渲染幾何圖形來處理碰撞形狀. 通常情況下, 這對終端使用者來說並不明顯, 但可以大大提高性能.

  • 試著從物理學中移除物體, 當它們不在視野中/在目前區域之外時, 或者重新使用物理學物件(例如, 也許你允許每個區域有8個怪物, 並重新使用這些怪物).

物理的另一個關鍵方面是物理週期率。在一些遊戲中,你可以大大降低週期率,比如說,你可以不用每秒更新物理 60 次,而只需每秒更新 30 次甚至 20 次。這樣可以大大降低 CPU 的負載。

改變物理週期率的缺點是,當物理學更新速率與每秒渲染的影格數不配對時,你可能會出現運動抖動或抖動。另外,降低物理週期率會增加輸入延遲。建議在大多數以玩家即時移動為特色的遊戲中,堅持使用預設的物理週期率(60 Hz)。

解決抖動的方法是使用*固定時間步長插值*,這涉及到平滑多個影格的渲染位置和旋轉,以配對物理。你可以自己實作,或者使用`協力廠商外掛程式 <https://github.com/lawnjelly/smoothing-addon>`__ 。從性能上來說,與運作物理週期相比,插值是一個非常廉價的操作。它的速度快了好幾個數量級,所以這在減少抖動的同時也帶來了部分顯著的性能提升。