Up to date

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

通用選項

前言

在一個理想的世界裡, 電腦將以無限的速度運作. 我們唯一的限制是我們的想像力. 然而, 在現實世界中, 製造出能讓最快的電腦也屈服的軟體實在是太容易了.

因此, 設計遊戲和其他軟體是在我們希望可能的情況下, 和在保持良好性能的前提下, 能夠實際實作的情況之間的折中.

要達到最佳效果, 我們有兩種方法:

  • 工作更快.

  • 工作更智慧。

我們最好將兩者混合使用.

節點與資源

更聰明地工作的一部分是認識到, 在遊戲中, 我們經常可以讓玩家相信他們所處的世界比實際情況要複雜得多, 互動性強, 圖形上也更刺激. 一個好的程式師是一個魔術師, 應該努力學習行業的技巧, 同時努力發明新的技巧.

緩慢的本質

在外界觀察者看來, 業績問題往往被歸納在一起. 但實際上, 業績問題有幾種不同的型別:

  • 每一影格都發生的緩慢過程, 導致持續的低影格率.

  • 一個斷斷續續的過程, 造成緩慢的到達 "巔峰", 導致停滯不前.

  • 在正常遊戲之外發生的緩慢程序, 例如載入關卡時.

每一種都會給使用者帶來煩惱, 但方式不同.

效能

對於優化來說, 最重要的工具可能是衡量性能的能力--找出瓶頸所在, 並衡量我們突破瓶頸的嘗試是否成功.

有幾種衡量性能的方法, 包括:

要非常清楚, 不同區域的相對性能在不同的硬體上會有所不同. 在一個以上的裝置上測量計時通常是個好主意. 如果你的目標是移動裝置, 情況尤其如此.

限制

CPU分析器通常是測量性能的常用方法. 然而, 它們並不總是能反映全部情況.

  • 瓶頸往往在GPU上,"由於"CPU給出的指令.

  • 由於在Godot中使用的指令(例如, 動態記憶體分配)"導致" 作業系統程序(在Godot之外)可能出現巔峰.

  • 由於需要進行初始設定, 您可能並不總是能夠對特定裝置進行配置, 例如手機.

  • 您可能需要解決您無法存取的硬體上出現的性能問題.

由於這些限制, 你經常需要使用偵測工作來找出瓶頸所在.

Detect 3d 選項

偵測工作對於開發人員來說是一項至關重要的技能(無論是在性能方面, 還是在錯誤修復方面). 這可以包括假設測試和二進位搜索.

型別轉換

比如說, 你認為精靈使你的遊戲速度變慢. 可以通過以下方式來驗證這個假設:

  • 當你新增更多的精靈或移除一些精靈時, 測量其性能.

這可能會引出一個進一步的假設: 精靈的大小是否決定了性能的下降?

  • 你可以通過保持一切不變, 但改變精靈的大小, 並測量性能來進行測試.

分析工具

分析器允許你在運作程式時對其進行計時. 然後, 分析器提供結果, 告訴你在不同的功能和區域所花費的時間百分比, 以及功能被呼叫的頻率.

這對於確定瓶頸和衡量改進的結果都非常有用. 有時, 改善性能的嘗試可能會適得其反, 導致性能變慢. 始終使用分析器和時長來指導您的工作

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

原則

SOLID

程式師浪費了大量的時間去考慮或者擔心程式中非關鍵部分的速度, 如果考慮到除錯和維護, 這些提高效率的嘗試實際上會產生強烈的負面影響. 我們應該忘掉小效率, 比如說97%左右的時間: 過早的優化是萬惡之源. 然而不應該放棄那關鍵的3%的機會

這些消息非常重要:

  • 開發者的時間是有限的. 與其盲目地試圖加快一個程式的所有方面, 應該集中精力在真正重要的方面.

  • 在優化方面的努力, 最終往往會得到比非優化程式碼更難閱讀和除錯的程式碼. 將這種情況限制在真正受益的領域更符合我們的利益.

僅僅因為我們 可以 優化某段程式碼, 並不一定意味著 應該 . 知道什麼時候優化, 什麼時候不優化, 是一項更好的技能.

這句話有一個誤導性的地方, 就是人們往往把注意力集中在 "過早的優化是萬惡之源 " 這句話上. 雖然過早的優化是不可取的, 但高性能的軟體是高性能設計的結果.

效能

鼓勵人們在必要時忽略優化的危險在於, 它很方便地忽略了考慮性能的最重要時間是在設計階段, 甚至在一個鍵碰到鍵盤之前. 如果一個程式的設計或演算法是低效的, 那麼以後再多的細節修飾也不會使它運作得很快. 它可能運作得更快, 但永遠不會像為性能而設計的程式那樣快.

這在遊戲或圖形程式設計中往往比在一般程式設計中更為重要. 一個高性能的設計, 即使沒有低水平的優化, 通常也會比一個低水平優化的平庸設計快很多倍.

漸進更新/修補

當然, 在實踐中, 除非你事先有知識, 否則你不可能在第一次就拿出最好的設計. 相反, 你往往會對某一特定區域的程式碼做出一系列版本, 每一個版本都採取不同的方法來解決這個問題, 直到你得出一個滿意的解決方案. 重要的是, 在你最終確定整體設計之前, 在這個階段不要在細節上花費太多時間. 否則, 你的很多工作都會被淘汰.

很難給出高性能設計的一般規範,因為這與問題本身有很大關係。不過有一點值得一提,在 CPU 方面,現代 CPU 幾乎總是受到記憶體頻寬的限制。這導致了面向資料的設計的重新興起,涉及到圍繞資料的*快取本地性*(cache locality)和線性存取進行資料結構和演算法的設計,避免在記憶體中進行跳轉。

動畫

假設我們有一個合理的設計, 聽取Knuth的教訓, 優化的第一步應該是找出最大的瓶頸--最慢的功能, 可輕鬆實作的目標.

一旦我們成功地提高了最慢區域的速度, 它可能就不再是瓶頸了. 因此, 我們應該再次進行測試/分析, 找到下一個需要關注的瓶頸.

因此, 該過程是:

  1. 分析和確定瓶頸.

  2. 最佳化 APK 大小

  3. 返回步驟1.

最佳化 APK 大小

有些分析器甚至會告訴你一個函式的哪個部分在減慢速度(哪些資料存取, 計算).

與設計一樣, 你應該首先集中精力確保演算法和資料結構是最好的. 資料存取應該是局部的(以最好地利用CPU快取), 而且使用緊湊的資料儲存通常會更好(同樣, 總是對測試結果進行分析). 通常情況下, 你會提前預計算繁重的計算. 這可以通過在載入關卡時執行計算, 載入包含預計算資料的檔或簡單地將複雜的計算結果儲存到腳本常數中並讀取其值來實作.

如果確認演算法和資料沒有問題,你通常可以在常式中做一些小的改變來提高性能。例如,可以將一些計算移到迴圈之外,或者將巢狀的 for 迴圈轉化為非巢狀的迴圈。(如果你事先知道 2D 陣列的寬和高,應該就是可行的。)

每次更改後, 一定要重新測試您的時長和瓶頸. 有些改變會提高速度, 有些則可能會產生負面效果. 有時, 一個小的積極效果會被更複雜的程式碼的負面效果所抵消, 可以選擇不做這種優化.

附錄

瓶頸數學

諺語 "一條鏈子的強度取決於其最薄弱的環節 " 直接適用於性能優化. 如果你的專案90%的時間都花在功能 A 上, 那麼優化 A 就會對性能產生巨大影響.

A: 9 ms
Everything else: 1 ms
Total frame time: 10 ms
A: 1 ms
Everything else: 1ms
Total frame time: 2 ms

在這個例子中, 將這個瓶頸 A 改進9倍, 總體影格時間減少5倍, 同時每秒影格數增加5倍.

但是, 如果其他東西運作緩慢, 也給你的專案帶來了瓶頸, 那麼同樣的改進只會帶來不那麼顯著的收益:

A: 9 ms
Everything else: 50 ms
Total frame time: 59 ms
A: 1 ms
Everything else: 50 ms
Total frame time: 51 ms

在這個例子中, 儘管我們對函式 A 進行了大量的優化, 但實際的影格率收益卻相當小.

在遊戲中, 事情變得更加複雜, 因為CPU和GPU彼此獨立運作. 你的總影格時間是由兩者中較慢的那一個決定的.

CPU: 9 ms
GPU: 50 ms
Total frame time: 50 ms
CPU: 1 ms
GPU: 50 ms
Total frame time: 50 ms

在這個例子中, 我們又對CPU進行了大量的優化, 但是影格數並沒有提高, 因為是GPU瓶頸.