《图解 C# 教程 第5版》与性能优化
这本书仍然是入门 C# 最好的一本书。
这本书新版出来的时候我十分关注,于是英子姐送了一本给我,本文也是答应英子姐所写的一篇文章。她一开始还问我“你现在还需要看这本入门书吗?”,我认为是的。工作了遇到了不少问题,大都跟自己基础不牢有关系。
这本书以图形为载体,生动地介绍了 C# 语言本身。其中图形对我们了解 C# 语法在内存中的本质十分有帮助,异步、异常等章节中的处理流程图也很清晰明了,这也是我看重的一点。
本文会从本书出发,简单讲讲代码优化的一些点。
在内存中的形态
为什么要了解 C# 在内存中的形态呢?
书中第四章介绍内存区域的栈中,有一句话说的很好:
作为程序员,你不需要显式地对它做任何事情。但了解栈的基本功能可以更好地了解程序在运行时在做什么,并能更好地了解 C# 文档和著作。
游戏开发中,除了业务逻辑,我们还会更关注游戏的性能本身。我们需要保证游戏能流畅运行在大部分机型上,保证每一帧能流畅地播放,例如 CPU 需要处理渲染代码、物理模拟、动画回调等等,其中我们的代码也有可能引起性能问题。我们需要更了解执行代码的代价,例如:
- 这些代码产生了多少 GC
- GC 只会产生一次还是每帧都会产生
- 在极端情况下代码的性能如何
- 是否使用了正确的数据结构
- Unity API 或者一些库 API 的背后到底做了什么
- …
这本书相对上一版多了 .Net Core, C# 7.0 语法的讲解,对于我而言,重温的是第 4、7、11、13、15、17、19 和 27 章节,这些内容是我工作中经常要接触、着手优化的地方。书中对于异步编程也介绍地很好,但对于我来说,反射、异步编程、新增语法等到以后有需要再看也不迟。
脚本的性能优化,无非是用更合适的代码去实现需求,不必要的内存都给我吐出来!(注:作者在生活中并没有这么吝啬)
下面会列举一些代码写法的性能对比。
结构和类
这其实也是用栈还是用堆的考量。
垃圾回收
Unity 用的是 mono 虚拟机,其堆的内存是通过垃圾回收算法 Boehm GC 来管理的,其不分代(Non-generational)和非压缩式(Non-compacting)的特性,导致了我们平常要注意避免加载过多的小内存,从而内存碎片化(Memory fragmentation)。
- 分代:大块内存、小内存、超小内存分在不同内存区域来进行管理。此外还有长久内存,当有一个内存很久没动的时候会移到长久内存区域中,从而省出内存给更频繁分配的内存。
- 压缩式:当有内存被回收的时候,压缩内存会把下图空的地方重新排布。

- 内存碎片化:内存过多小内存,导致大内存不能有效地被使用。

具体可以参考 Unity 文档 Understanding the managed heap
同时也推荐高川老师的演讲:浅谈 Unity 内存管理,和我看视频时的笔记:笔记。
用结构还是类
这里推一篇微软官方文档:Choosing Between Class and Struct
引用类型被分配在堆上并被垃圾回收算法管理,值类型则分配在栈上,栈会按需 unwind 来释放他们。因此,值类型的释放比引用类型的释放开销要小。
书中 11.9 小节还提到:
对结构进行分配的开销比创建类实例小,所以使用结构代替类有时可以提高性能,但要注意装箱和拆箱的高昂代价。
值类型数组的分配和释放比引用类型数组的分配和释放开销也更小。
除了最基本的修改值类型和引用类型的区别外,要注意的是传递参数或者返回返回值的时候,值类型都会隐性地被创建,这可能也会产生没想到的内存开销。
从 .Net 内存分配成本的角度来说,类的对象储存的内存首先需要分配 4 个字节作为对象头字节(object header word),跟着再分配 4 个字节作为方法表指针(method table point...
剩余内容已隐藏