Unity3d中的细节优化 第一篇
1.Instantiate实例化操作技巧。
将频繁需要实例化,销毁的对象放入缓存池,可以大量减少实例化,销毁的开销。将部分耗时资源进行预载。
2.复杂的UI界面和带有动画组件的GameObject不要频繁切换Active/Deactive
因为设置Active/Deactive复杂对象所用的耗时会比较大(特别是包含Animator和Animation每次GameObject Active时会有初始化耗时,越复杂耗时越大)。那么如何替换掉Deactive操作呢?我们可以将需要Deactive的操作变成将GameObject移动到比较远的,摄像机之外。然后将GameObject上面的全部Component的enabled属性设置成false。Active时再重新设置回来。(此技巧不适用于NGUI,具体原因请移步查看UI优化的技巧)
3.Mono中for与foreach比较
foreach每次调用会产生有40Byte的GC数据(这个是 Mono 的一个 bug),产生GC 的根本原因是使用了 using 语句。(GetEnumerator()返回值类型,在using 中装箱了),而使用while和for两种方式不产生 GC。所以在实测情况下,在目前的项目中,foreach 还是有 GC 的。(不管是 IList 还是 ArrayList都会有GC)
而在处理Dictionary 遍历的时候,我们可以这样:
var enumerator = m_Dictionary.GetEnumerator();
while (enumerator.MoveNext())
{
var element = enumerator.Current;
...
}
4.关于Dictionary查找时的一些细节
在很早之前,我们查找Dictionary中一个不确定的选项时都会这么写:
Dictionary<Key, Value> dict = new Dictionary<Key, Value>();
...........
if (dict.ContainsKey(key))
{
Value value = dict[key];
}
但是其实查看Dictionary的源码,这样的操作其实会调用两次FindEntry()函数,那么在MS.Net2.0里,已经新增了函数TryGetValue,所以推荐大家的写法是:
Dictionary<Key, Value> dict = new Dictionary<Key, Value>();
...........
Value value;
if (dict.TryGetValue(key, out value))
{
value.......
}
使用TryGetValue,FindEntry 只调用了一次。
5.在Mono下,字符串拼接
连接次数只有几次的话(10 以内),应该直接用 + 号连接,实测并不产生 GC,因为编译器进行了优化。
其余的使用 StringBuilder,StringBuilder 内部 Buffer 的缺省值为 16,按 StringBuilder 的使用场景,Buffer 肯定得重新分配。经验值一般用 256 作
为 Buffer 的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定 Buffer 的初值。使用 new StringBuilder(256) 就将 Buffer 的初始长度设为了256。
Struct与Class使用对比1
Struct和Class的有以下几个基本的不同点:
类是引用类型,结构是值类型。
结构不支持继承。
结构中声明的字段无法赋予初值,类可以。
结构不能声明默认的构造函数。
结构的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制 。
把他们放一起对比的真正目的是想知道,用那种数据能更充分地利用值类型和引用类型,让效率最高(即装箱或拆箱少,堆内存少)。
Struct在栈中不产生 GC,class在堆中,会产生 GC。
对 Struct 的结点修改时,改完以后记得重新赋值。因为 Struct 赋值是copy而不是引用,完以后,以前的不生效。
使用结论
类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。因此,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。但假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。
6.数组,ArrayList,List<T> 的区别
数组:内存中是连续存储的,索引速度非常快,赋值与修改元素也很简单。但不利于动态扩展以及移动。因为数组的缺点,就产生了 ArrayList。
ArrayList:使用该类时必须进行引用,同时继承了 IList 接口,提供了数据存储和检索,ArrayList对象的大小动态伸缩,支持不同类型的结点。
ArrayList虽然很完美,但结点类型是 Object,故不是类型安全的,也可能发生装箱和拆箱操作,带来很大的性能耗损。
List是泛型接口,规避了 ArrayList的两个问题。
7.Enum的使用技巧
在Mono中枚举如果使用不当是会产生GC的,下面我们说下如何避免
1.不要把枚举当Dictionary的key使用
2.不要把枚举转成 string,会有装箱发生,从而产生GC
8.闭包的使用技巧
闭包是指有权访问另一个函数作用域中的变量的函数。闭包这个词本身指的是一种函数,而创建这种特殊函数的一种常见方式是在一个函数中创建另一个函数,例如委托delegate。
使用闭包的时候,涉及到大约 120B 的GC,随着闭包引用内容的增加而增大。
闭包在使用时会要求函数体能够访问方法范围之外的变量状态,而引用的内容越多,则产生的GC越大。
在使用时,最好尽可能避免Unity3d中的闭包,尤其是在基于每帧执行的代码中,如果确定使用,则尽量减少外部变量的使用。