在线阅读:
参考文章:
数据局部性介绍了计算机的存储层次以及如何使用其以获得优势。 脏标识帮你避开不必要的计算。 对象池帮你避开不必要的内存分配。 空间分区加速了虚拟世界和其中元素的空间布局。
数据局部性
合理组织数据,充分使用CPU的缓存来加速内存读取
模式
现代的CPU有缓存来加速内存读取。 它可以更快地读取最近访问过的内存的毗邻内存。 通过提高内存局部性来提高性能——保证数据以处理顺序排列在连续内存上。
脏标识模式
将工作延期到需要其结果时才去执行,避免不必要的工作
模式:
一组原始数据随着时间变化而改变。 使用代价昂贵的过程推定一组导出数据。 用一个“脏”标识追踪导出数据是否与原始数据保持一致。 它在原始数据改变时被设置。 如果导出数据被请求时,该标识被设置了,那么重新计算并清除标识 否则的话,使用之前缓存的导出数据。
什么时候清空脏标识
-
当结果被请求时:
-
如果不需要结果,可以完全避免计算。 如果原始数据变化的速度比推导数据获取的速度快得多,这效果很明显。
-
如果计算消耗大量时间,这会造成可察觉的卡顿。 将工作推迟到玩家想要结果的时候会严重影响游戏体验。 这部分工作一般足够快,不会构成问题,但是如果构成问题,你就需要提前做这些工作。
-
-
在精心设计的检查点处:
有时候,会有某个时间点适合这么做,或在游戏过程中某个自然适合处理推迟计算的时机去做。 例如,只有海盗船驶入港口才会去保存游戏。 如果同步点不是游戏的机制,我们会将这些工作隐藏在加载画面或者过场动画之后。
-
这种工作不会影响到玩家体验。 不像前一个选项,你总会在游戏紧张运行时打断玩家的注意力。
-
在工作时丧失了控制权。 这和前一个选项相反。你在处理时能进行微观控制,确保有效且优雅地处理它。
你不能保证玩家真的到了检查点或者满足了定义的条件。 如果他们在游戏中迷失了,或者游戏进入了奇怪的状态,最终工作会推迟得超乎预料的晚。
-
-
在后台处理:
通常情况下,你在第一次更改时启动固定时长的计时器,然后在计时器到时间后处理之间的所有变化。
-
可以控制工作进行的频率。 通过调节计时器,可以保证它发生得像预期一样频繁(或者不频繁)。
-
更多的冗余工作。 如果原始状态在计时器运行之间只改变了很少的部分,最终处理的大部分都是没有改变的数据。
-
需要支持异步工作。 在“后台”处理数据意味着玩家可以同时继续做事。 这就意味着你将会需要线程或者其他并行支持,这样游戏在处理数据的同时仍然可以继续游玩。
由于玩家很可能与处理中的状态交互,你也需要考虑保持并行修改的安全性。
-
对象池模式
放弃单独地分配和释放对象,从固定的池中重用对象,以提高性能和内存使用率
模式
定义一个池对象,其包含了一组可重用对象。 其中每个可重用对象都支持查询“使用中”状态,说明它是不是“正在使用”。 池被初始化时,它就创建了整个对象集合(通常使用一次连续的分配),然后初始化所有对象到“不在使用中”状态。
当你需要新对象,向池子要一个。 它找到一个可用对象,初始化为“使用中”然后返回。 当对象不再被需要,它被设置回“不在使用中”。 通过这种方式,可以轻易地创建和销毁对象而不必分配内存或其他资源。
何时使用
-
需要频繁创建和销毁对象。
-
对象大小相仿。
-
在堆上进行对象内存分配十分缓慢或者会导致内存碎片。
-
每个对象都封装了像数据库或者网络连接这样很昂贵又可以重用的资源。
重用对象不会自动清理
很多内存管理系统拥有debug特性,会清除或释放所有内存成特定的值,比如0xdeadbeef
。
空间分区
将对象根据它们地位置存储在数据结构中,来高效的定位对象
模式
对于一系列对象,每个对象都有空间上的位置。 将它们存储在根据位置组织对象的空间数据结构中,让你有效查询在某处或者某处附近的对象。 当对象的位置改变时,更新空间数据结构,这样它可以继续找到对象。