Unity资源加载速度研究

对于一款游戏来说,如果想要一个好的游戏体验需要一个稳定帧数。如果游戏跑60帧的话,那每帧只有16.67ms,但很多时候加载一个资源就花费了100ms甚至1000ms。这就是正常游戏中常见的卡顿问题,资源加载引起的卡顿带来很多的困扰,如何提高资源在速度是一个有意义的问题。由于不了解资源加载类背后的实现逻辑,这里通过实验来推测影响资源加载的因素。

下面分享一个简单的实验数据,来认识资源结构对加载速度的影响,首先看定义的数据类。

public class SingleData : MonoBehaviour {
    public int data;
    public string strData;
}
public class SingleDataArray : MonoBehaviour
{
    public List<int> data;
    public List<string> strData;
}

然后生成需要的数据,这里为了使结果对比明显设置对象数量为1W个。

第一个数据类型为多数据,单GameObject对象,一个GameObject挂载1W个mono。

static void MultData_OneObject()
{
    GameObject go = new GameObject("MultData-OneObject");
    GameObject child = new GameObject("ChildData");
    child.transform.parent = go.transform;
    for (int i = 0; i < MAX_COUNT; ++i)
    {
        SingleData data = child.AddComponent<SingleData>();
        data.data = i;
        data.strData = i.ToString();
    }

    PrefabUtility.CreatePrefab(PATH_ROOT + "MultData_OneObject.prefab", go, ReplacePrefabOptions.ReplaceNameBased);
}

第二个数据类型为多数据,多GameObject对象,每个GameObject挂载1个mono。

static void MultData_MultObject()
{
    GameObject go = new GameObject("MultData-MultObject");
    GameObject child = new GameObject("ChildData");
    child.transform.parent = go.transform;
    for (int i = 0; i < MAX_COUNT; ++i)
    {
        GameObject subGo = new GameObject("subGo" + i);
        subGo.transform.parent = child.transform;
        SingleData data = subGo.AddComponent<SingleData>();
        data.data = i;
        data.strData = i.ToString();
    }

    PrefabUtility.CreatePrefab(PATH_ROOT + "MultData_MultObject.prefab", go, ReplacePrefabOptions.ReplaceNameBased);
}

第三个数据类型为数据数组,单GameObject对象,数据数组参见SingleDataArray 定义。

static void DataArray_OneObject()
{
    GameObject go = new GameObject("DataArray-OneObject");
    GameObject child = new GameObject("ChildData");

    child.transform.parent = go.transform;
    SingleDataArray data = child.AddComponent<SingleDataArray>();
    data.data = new List<int>();
    data.strData = new List<string>();

    for (int i = 0; i < MAX_COUNT; ++i)
    {
        data.data.Add(i);
        data.strData.Add(i.ToString());
    }

    PrefabUtility.CreatePrefab(PATH_ROOT + "DataArray_OneObject.prefab", go, ReplacePrefabOptions.ReplaceNameBased);
}

第四个数据类型为数据数组,多个GameObject对象,数组数据挂载在其中一个GameObject上。

static void DataArray_MultObject()
{
    GameObject go = new GameObject("DataArray-MultObject");
    GameObject child = new GameObject("ChildData");

    child.transform.parent = go.transform;
    SingleDataArray data = child.AddComponent<SingleDataArray>();
    data.data = new List<int>();
    data.strData = new List<string>();

    for (int i = 0; i < MAX_COUNT; ++i)
    {
        data.data.Add(i);
        data.strData.Add(i.ToString());

        GameObject subGo = new GameObject("subGo" + i);
        subGo.transform.parent = child.transform;
    }

    PrefabUtility.CreatePrefab(PATH_ROOT + "DataArray_MultObject.prefab", go, ReplacePrefabOptions.ReplaceNameBased);
}

然后就是测试资源加载速度了,这四类数据数据内容相似有较大的对比意义。下面的测试数据验证了一些想法,但是结果还是和预想有较大的差异。

MultData_OneObject

TotalCount=10004,FileSize=1099KB,AssetBundleSize=392KB
PC Resources.Load 2058ms, Instantiate 1758ms
PC AssetBundle.LoadFromFile 3ms,AssetBundle.Load 2325ms,Instantiate=1797ms

MultData_MultObject

TotalCount=30004,FileSize=3677KB,AssetBundleSize=1276KB
PC Resources.Load 414ms, Instantiate 61ms
PC AssetBundle.LoadFromFile 10ms,AssetBundle.Load 1481ms,Instantiate=72ms

DataArray_OneObject

TotalCount=5,FileSize=122KB,AssetBundleSize=81KB
PC Resources.Load 0ms, Instantiate 2ms
PC AssetBundle.LoadFromFile 0ms,AssetBundle.Load 2ms,Instantiate=2ms

DataArray_MultObject

TotalCount=20005,FileSize=2732KB,AssetBundleSize=853KB
PC Resources.Load 262ms, Instantiate 25ms
PC AssetBundle.LoadFromFile 7ms,AssetBundle.Load 782ms,Instantiate=29ms

数据分析

数据比较多,也只是测试了在PC上执行的速度。由于资源是放在SSD上的所以在I/O不会出现性能问题,结果可以认为是CPU的开销。手机上由于I/O以及更弱的CPU加载速度回更慢,不过这里的数据对比已经非常有参考意义与价值了。

对比MultData_OneObject和MultData_MultObject可以发现单个GameObject挂载1W的mono对性能有极大的影响,而1W个GameObject每个各挂1个mono则有较好的表现。虽然在加载上依旧很慢,但是资源实例化(Instantiate)的提升是显著的。

接着对比MultData_MultObject和DataArray_MultObject可以发现,通过把mono的数据合并到一个对象,并以数组的方式存储也能对资源加载和资源实例化有一个较大的提升。

最后对比DataArray_MultObject和DataArray_OneObject可以发现,当把1W个无用的GameObject对象移除后的资源加载和资源实例化有一个极大的提升,即使同步加载这么多数据也不会出现什么问题了。

通过对比不同资源组织方式对性能的影响,我们清晰的认识到了一些对资源性能影响的关键性要素。对象数量极大的影响资源加载速度,在AssetBundle打包后更加明显。同时GameObject数量对资源加载速度的影响要大于mono对象对加载速度的影响。最后如果一个GameObject挂载过多的mono则会导致资源加载与资源实例化性能急剧降低。

更进一步

在了解对象数量对加载速度影响后,我们想通过把资源按数组的方式组织来提升资源加载速度。然而很多情况下这是很难达成的,也有诸多不方便的地方。

之后在Unity文档中发现了ScriptableObject能帮助我们减少对象数量提高加载速度,同时还能帮助我们节省内存。

ScriptableObject is a class that allows you to store large quantities of shared data independent from script instances. Do not confuse this class with the similarly named SerializableObject, which is an editor class and fills a different purpose. Consider for example that you have made a prefab with a script which has an array of a million integers. The array occupies 4MB of memory and is owned by the prefab. Each time you instantiate that prefab, you will get a copy of that array. If you created 10 game objects, then you would end up with 40MB of array data for the 10 instances.

ScriptableObject主要的作用是帮助减少重复对象,把数据直接以资源的形式保存以复用资源。当然比较好的做法是在制作的时候用mono保存数据,最后导出为文本数据。这样也能极大的提高资源加载速度。

不同的资源加载方式对资源加载速度也有较大差异,资源的形式也是多种多样的。通过对不同事物建立对应模型并测试,可以帮助认识事物、改善事物。最近比较忙没做资源加载方式对不同资源类型资源的影响,有兴趣的同学可以尝试下,相信会有很大的收获。

[完 2017-07-23 Carber]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容