一、客户端的缓存与缓存替换机制
客户端的资源缓存:
在客户端游戏中,通常有大量的资源要处理,这些可能包括贴图、动作、模型、特效等等,这些资源往往存在着磁盘文件->内存(->显存)的数据通路,因为从磁盘文件读入内存是一个昂贵的的操作,所以很多客户端中的处理方法是放入一个缓存,当读入一个文件到内存后,就在内存中为他暂时保留一段时间,下次在读入这个文件就不用重复从磁盘读了,这种优化带来了客户端效率的极大提升,比如在一个有上百玩家的场景,可能我们用的玩家模型资源都是一个,只是贴图不同而已,如果使用缓存,那么你只需要从磁盘读取一次玩家模型的文件,而如果不使用,你可能要反复读取上百次,IO操作的低效会让用户很卡。
缓存替换:
然而我们用户的内存是有限大的,如果我们把每个资源都读进去保留而不释放,游戏就会越来越吃内存,一个典型的大象网游的资源可能几个G,最坏的情况用户读入了所有资源到缓存而不释放,那么也会因为内存不够用而崩溃。所以这里就通常要选择一些缓存替换机制:即动态的释放和加入资源到缓存,有进有出,但是要保证尽量让用户IO操作减少,一种最乐观的情况是,存在缓存中的资源恰好是用户使用的最多的资源,使用较少的会被使用多的交换出去。这模仿了CPU的cache机制。
二、现有的一些缓存替换机制:
基于CPU和操作系统的一些经典的cache replacement算法,客户端的资源缓存替换机制有以下一些做法:
1.懒人型,最简单的。就是没什么交换算法,只管读入缓存,然后只是定期的或发现缓存太大时一次性的清空缓存重来,这种在清空后会经历一段加载资源的顿卡,突然发现目前我们游戏的机制就是这样的,尴尬。。。但是这可能符合简单粗暴实用的原则。
2.LRU算发:大名鼎鼎的算法,当出现替换时,总是替换那个 上次使用时间距离限制最远的那个,这种算法认为最近使用的就是最长使用的,这种算法被多数体系结构使用,不过在某些极端情况下会导致thrash(内存的剧烈颠簸),比如说当一段时间内需要加载的东西过多时,而加载的资源呈有序重复序列时可能缓存会被一遍遍的清空。
3.MRU算法:它与LRU对应,但是它总是交换出最近使用的那个,这个倾向于保存最老的那个,听起来很奇怪,但是它往往不单独使用,通常配合LRU使用,当LRU出现thrash时转到MRU算法会有所帮助
三、基于Age和Cost 的算法:
今天看到了一种算法基于Age和Cost,似乎对客户端的缓存替换研究的更加到位一些,这种算法梗概如下:
1.Age:
为每个缓存保存一个32位的Age值初始为00000000(也就是第一次载入缓存时)(文档暂以8位代替),,每当hit一次,当前的最低位置1,每个计数区间(或每一帧)这个Age左移一位,例如某个资源在几帧内Age的变化
frame1:00000001(used)
frame2:00000011(used)
frame3:00000111(used)
frame4:00011100(not used)
frame5:00111000(not used)
根据当前的age,可以算出Age percentage Cost APC=1的数量/总的位数
APC越高说明这个资源近段时间是越有用的,替换时选择APC低的替换
基于Age的算法更加精确的得出了最近一段时间内资源的使用率,找出比lru更合理的替换,
2.Age扩展算法:
上面的每一步是按照帧来算的,实际使用中没帧来统计肯定是有点过了,可以按照一定时间片来统计,在一定时间片内可能一个资源回被用到n次,因此扩展的Age算法就单纯的标记1改为标记n,即这个统计区间内被用到的次数,如上例
time1:00000005(used 5 times)
time2:0000005A(used 9 times)
time3:000005A1(used 1 times)
time4:0005A100(not used)
time5:005A1000(not used)
这样APC=各位的值相加/最大的可能值
例如某个模型资源在上一段时间内被100个玩家引用,那么替换他的代价就明显要高很多,Age的扩展方法考虑的引用的频次
3.Age&Cost:
在实际的操作中,有些资源的替换成本是非常大的,例如从硬盘读入一个10m的贴图显然比读入一个100k的贴图成本大很多,所以替换时不能相同对待,这里考虑的成本记为relative cost(RC),算法中将最终的缓存替换成本TC=APC*RC,这样来综合考虑,通常将APC取值区间规整为0-1,而将rc规整为 1-10(算法作者认为载入文件的时间成本权重更大)。这样对不同的资源为它赋值不同的载入时间成本RC,再乘以上面的Age算法中的APC(引用频率成本),综合得出了它的缓存替换成本。一种比较简单的rc的赋值可以用它的文件大小来自动赋值。
几个例子:
APC RC TC
1 10 10 这说明这个文件的替换成本非常大,主要是文件比较大,同时它引用的频率特别高,这种资源基本上不会轻易从缓存中替换出来
0.2 8 4 这个文件很大,但是引用频率还很低,所以替换程度是中等的
1 5 5 文件很小,说明载入很快,但是引用频率很高,所以可以替换出来(因为再载入的成本低),也可以不替换出来,中等的
0.1 10 1 这个文件很大,载入很慢,但是引用很少,也就是说内存中存在一个很占地方但是几乎不怎么用的资源,值得替换出来,节省空间,因为不常用再次载入就算费时也值得
比如我们的游戏中开场有一个非常大的贴图资源,用于登录界面,它最开始有60M,(游戏中一般的贴图在几百K以下),而它在游戏中不会再被用到,现有的算法这个很大的资源会一直占在缓存里,用户内存被无故吃了很多,但是应用本算法,它的RC在游戏中可以达到上限10,但是APC基本上是下限,也许0.01,所以他的TC可能只有0.1,将登陆之后很快的时间内被从内存中释放出来。
赶紧利用这个算法节省一下我们的内存