游戏设计模式阅读笔记16——优化模式(数据局部性)

本文讨论了如何通过优化数据局部性提升游戏性能,解释了CPU缓存的工作原理和数据局部性的概念。建议在遇到性能问题时考虑优化,并提供了三种实践策略:使用连续数组、打包数据和冷/热数据分割。同时,针对多态处理和游戏实体定义提出了不同的解决方案,以减少缓存不命中,提高内存访问效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、意图

二、动机

三、数据局部性

        1.简介

        2.何时使用:

        3.注意:

四、实例代码

        1.连续数组

2.打包数据

3.冷/热分割

五、问题

       1.怎么处理多态

        2.游戏实体是如何定义的?

                1.如果游戏实体是拥有它组件指针的类

                2.如果游戏实体是拥有组件ID的类

                3.如果游戏实体本身是一个ID


一、意图

        合理组织数据,充分使用CPU的缓存来加速内存读取。

二、动机

        随着CPU速度的递增,我们可以更快的处理数据,但是不能更快的获得数据。

         所以一般会从内存获取数据加载到寄存器。

       比如:会计需要仓库的文件统计数据。但是仓库有专门的管理员。由于管理员一天只找得到一盒文件。所以不管会计速度多快,一天也就只能统计一盒数据。

        优化:如果仓库管理员找到目标数据时,还把周围相关的数据都拿来放在桌子上。正好会计需要的下一盒数据正在里面。那效率就提高了。

这里会计是CPU,会计的桌子就是寄存器,仓库是机器的RAM。仓库管理员就是从主存加载数据到寄存器的总线。

        当然,现在就有了优化:CPU缓存技术。芯片内部有一小块存储器。CPU从那里取数据比内存快得多。这个内存使用的是静态RAM,也被称为缓存。(芯片上的被称为L1级缓存)

        无论何时芯片需要从RAM获取一字节的数据,它就会自动将一整块内存读入然后放入缓存——通常是64到128字节。这些一次性传输的字节被称为cache line。

           

        如果需要的下一字节数据就在这块上,CPU就可以从缓存中直接读取。成功从缓存中找到数据被称为“缓存命中”。如果不能从中获得而得从主存里取,就是一次缓存不命中。缓存不命中,CPU就会空转。

        为了能从cache line读取越多需要的东西,就需要优化组织数据结构,让要处理的数据紧紧相邻。

三、数据局部性

        1.简介

        现代CPU有缓存来加速内存读取。它可以更快地读取最近访问过的内存的毗邻内存。通过提高内存局部性来提高性能——保证数据以处理顺序排列在连续内存中。

        2.何时使用:

        第一准则:在遇到性能问题时使用。优化代码不会让你过得更轻松,因为其往往更加复杂且不灵活。

        确定性能问题是由缓存不命中引发。

        3.注意:

        在C++中,使用指针就意味着在内存中跳跃 ,就带来了不可避免的缓存

四、实例代码

        1.连续数组

        比如需要更新每个敌人的AI,声音,渲染:

        这段代码一看没有问题,但是这里每次去的游戏实体组件的指针就是一次缓存不命中。并且每次获取不同游戏实体也是一次缓存不命中。

        这里优化这两个不命中:可以将每种组件存入巨大的数组:一个AI数组,一个物理数组,一个渲染数组。这里存的是实体不是指针。

        这样优化后,就不是在内存中跳来跳去,而是在数组中做直线遍历。

 

2.打包数据

       假设做一个粒子系统,将所有的粒子放在一个巨大的缓存中。更新方法:

         这里检查粒子的标志位,也会将粒子的数据都加载进来,不活跃的粒子越多,就需要跳过的部分越多,就又造成了颠簸缓存了。

        优化:提前排序粒子,将活跃粒子放在列表前面。也就不用检车标识位了。

         这里不需要每帧都去排序,只需要在激活粒子与反激活粒子是去管理数组就可以了。

3.冷/热分割

        有些游戏实体有些状态时每帧都变化的,也有些是整个生命周期只变一次的。这里就可以将数据结构分离成两个部分,一部分保存“热”数据,那些每帧都要调用的数据。另一部分为冷数据,使用次数较少的。

        这里冷组件可以在热组件中包含一个指向它的指针。就可以达到不加载但又可以找到的目的。

        

五、问题

       1.怎么处理多态

                 i.在做内存优化的部分避免使用子类。

                 ii.为每种类型使用分离数组代替多态

                 iii.如果不太担心缓存,也可以使用指针的集合

        2.游戏实体是如何定义的?

                1.如果游戏实体是拥有它组件指针的类

                可以将组件实体存储在连续数组中,就优化了组件在内存中的组织。但是很难在内存中移动组件,如果在实体中有指针指向该组件,移动的时候就需要去同步更新指针。

                2.如果游戏实体是拥有组件ID的类

                使用裸指针的挑战在于在内存中移动它很难。可以使用更直接的方案:使用ID或者索引来查找组件。ID查找可以使用简单的遍历数组,也可以使用哈希表,将ID映射到组件现有位置。

                3.如果游戏实体本身是一个ID

                如果,所有的状态逻辑都到了各个系统中。游戏实体的存在意义就只是记录哪几个组件构成了这个游戏物体。在组件想交互时,提供别的组件的位置。

        这里的问题是查找某一个组件也许会很慢,解决办法是将组件在数组中的索引作为实体的ID。

                

 

 

 

        

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值