2017年10月8日 星期日

[Unity]Resources.Load效能測試

儘管官方自己都說不建議使用Resources資料夾, 但是在能取代其便利性的資源載入方案出現前, Resources資料夾仍是我們有快速開發需求時的好朋友

我們使用Resources資料夾內資源, 常用步驟為:
  1. 使用Resources.Load將資源讀取至記憶體
  2. 使用Object.Instantiate將步驟1實例化
然而我們在開發時很容易遇到一種需求:頻繁的生成同一種資源(ex:子彈)
在這種情況下一直Resources.Load同一個物件是必要的嗎?效能開銷又是如何呢?

讓我們來看看以下測試範例

測試專案: https://github.com/GooKu/Unity2017Test.git
測試碼: https://github.com/GooKu/Unity2017Test/blob/master/Assets/ResourcesLoadTest/ResourceLoadTest.cs
測試scene: ResourcesLoadTest.unity
環境資訊
CPU: i5-4460(3.2GHz)
VGA: AMD R7770-PMD1GD5
OS: Windows 10
Unity版本: 2017.1.0f3 Personal
Platform: PC Standalone
---------
此測試種使用了2種方法讀取Resources資料夾裡的3個不同prefab:
  1. 直接使用Resources.Load讀取
  2. 將Resources.Load的物件存進Dictionary裡, 要使用時再取出

擊點對應按鈕同時讀取10000次3個測試資源, 得到的結果如下

操作方法 時間(ms)
Resources.Load 39
Dictionary 1

我們可以發現使用Dictionary取出存起來的資源幾乎沒什麼時間開銷,
而每次都用Resources.Load讀資源所花費的時間是其數十倍

此結果為PC輸出版的測試時間, 直接在編輯器中測試時 Resources.Load的時間會少很多, 但仍為Dictionary的數倍

由此我們可以了解:
使用Dictionary作資源暫存會占用部分記憶體空間, 但可以有效的減少取出資源時的CPU開銷
因此適合用於容量不大但需要頻繁生成的資源
這便是常見的"以空間換時間"小技巧

最後附上以此概念衍生的優化範例:
    //使用一個Dictionary來存Resources.Load的東西
    private Dictionary<string, GameObject> m_dic = new Dictionary<string, GameObject>();
    //物件創建口, 此例直接用prefab的名字當參數
    private GameObject Create(string prefabName)
    {
        GameObject resource = null;
        //嘗試從m_dic取出資源
        if (!m_dic.TryGetValue(prefabName, out resource))
        {
            //如果找不到, 就Resources.Load一個
            resource = Resources.Load<GameObject>(prefabName);
            //並加入m_dic, 這樣之後就找的到了
            m_dic.Add(prefabName, resource);
        }
        //創建實體
        return GameObject.Instantiate(resource);
    }

沒有留言:

張貼留言