一、客户端的缓存与缓存替换机制

客户端的资源缓存:

在客户端游戏中,通常有大量的资源要处理,这些可能包括贴图、动作、模型、特效等等,这些资源往往存在着磁盘文件->内存(->显存)的数据通路,因为从磁盘文件读入内存是一个昂贵的的操作,所以很多客户端中的处理方法是放入一个缓存,当读入一个文件到内存后,就在内存中为他暂时保留一段时间,下次在读入这个文件就不用重复从磁盘读了,这种优化带来了客户端效率的极大提升,比如在一个有上百玩家的场景,可能我们用的玩家模型资源都是一个,只是贴图不同而已,如果使用缓存,那么你只需要从磁盘读取一次玩家模型的文件,而如果不使用,你可能要反复读取上百次,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,将登陆之后很快的时间内被从内存中释放出来。

赶紧利用这个算法节省一下我们的内存

基于年纪和成本(Age & Cost)的缓存替换(cache replacement)机制的更多相关文章

  1. 搜索引擎的缓存(cache)机制

    什么是缓存? 在搜索领域中,所谓缓存,就是在高速内存硬件设备上为搜索引擎开辟一块存储区,来存储常见的用户查询及其结果,并采用一定的管理策略来维护缓存区内的数据.当搜索引擎再次接收到用户的查询请求时,首 ...

  2. HTTP请求的缓存(Cache)机制

    原文地址:http://small.aiweimeng.top/index.php/archives/58.html 先来一张图: ####下面简单的来描述一下HTTP Cache机制: 当资源资源第 ...

  3. 一个基于STSdb和fastJson的磁盘/内存缓存

    一个基于STSdb和fastJson的磁盘/内存缓存 需求 业务系统用的是数据库,数据量大,部分只读或相对稳定业务查询复杂,每次页面加载都要花耗不少时间(不讨论异步),觉得可以做一下高速缓存,譬如用n ...

  4. 缓存篇(Cache)~大话开篇

    回到占占推荐博客索引 闲话杂淡 想写这篇文章很久了,但总是感觉内功还不太够,总觉得,要写这种编程领域里的心法(内功)的文章,需要有足够的实践,需要对具体领域非常了解,才能写出来.如今,感觉自己有写这种 ...

  5. 从Java视角理解CPU缓存(CPU Cache)

    从Java视角理解系统结构连载, 关注我的微博(链接)了解最新动态众所周知, CPU是计算机的大脑, 它负责执行程序的指令; 内存负责存数据, 包括程序自身数据. 同样大家都知道, 内存比CPU慢很多 ...

  6. Kafka元数据缓存(metadata cache)

    经常有人问的一个问题就是:Kafka broker到底是不是无状态的?网上有这样的说法: 正常情况下consumer会在消费完一条消息后线性增加这个offset.当然,consumer也可将offse ...

  7. yii中缓存(cache)详解

    缓存是用于提升网站性能的一种即简单又有效的途径.通过存储相对静态的数据至缓存以备所需,我们可以省去生成这些数据的时间.在 Yii 中使用缓存主要包括配置和访问缓存组件 . 内部方法 一.缓存配置: 1 ...

  8. Cache replacement policies 缓存实现算法

    Cache replacement policies - Wikipedia https://en.wikipedia.org/wiki/Cache_replacement_policies Cach ...

  9. yii中缓存(cache)详解 - 彼岸あ年華ツ

    缓存是用于提升网站性能的一种即简单又有效的途径.通过存储相对静态的数据至缓存以备所需,我们可以省去生成 这些数据的时间.在 Yii 中使用缓存主要包括配置和访问缓存组件 . 内部方法 一.缓存配置: ...

随机推荐

  1. C# ashx生成的验证码

    public void ProcessRequest(HttpContext context) { context.Response.ContentType = "image/jpeg&qu ...

  2. webform repeater

    repeater:由模板构成,解析后模板就不存在了             需要指定数据源进行数据绑定 List<Fruit> list = new FruitDA().Select(); ...

  3. hdu 2190

    //hdu2190 水题  题意是给一个n*3的教室,用1*1,2*2的砖去铺满,有多少种铺法,一开始没发现这个规律,想了一下,应该是递归. #include <iostream> usi ...

  4. (转)PHP 的 __FILE__ 常量

    今天碰到了PHP的常量__FILE__的问题了. 在网上查了一下.总结了以下规律. dirname(__FILE___) 函数返回的是脚本所在在的路径. 比如文件 b.php 包含如下内容: < ...

  5. Ext江湖笔记:JavaScript基本知识点

    1.基本对象:Number,String,Date,Array,Error,RegExp,Math,Boolean ps:本人基本使用java写代码,常常写出Number n = new Number ...

  6. 获取subview

    通常我们在view层级里面对subView的操作可以通过两种方式:1.保留一个subview的引用,然后在类中通过该引用对该subview进行操作,但是要注意在适当的位置添加内存维护的代码,退出前手动 ...

  7. Objective-C学习篇10—NSDate与NSDateFormatter

    NSDate NSDate 时间类,继承自NSObject,其对象表示一个时间点 NSDate *date = [NSDate date]; NSLog(@"date = %@", ...

  8. URAL 1008 - Image Encoding(bfs坑爹题)

    坑爹题,两种输入输出互相交换,裸bfs #include <stdio.h> #include <string.h> typedef struct { int x; int y ...

  9. IOS学习之十七:Grand Central Dispatch(GCD)编程基础

    IOS学习之十七:Grand Central Dispatch(GCD)编程基础   有过编程经验的人,基本都会接触到多线程这块. 在java中以及Android开发中,大量的后台运行,异步消息队列, ...

  10. AtomicInteger小小的理解

    这里仅仅是验证多线程环境下,AtomicInteger的安全性. 通过源码不难发现两点: 1.value属性是volatile修饰 2.使用了unsafe的CAS操作 通过上述两点,实现非阻塞同步(乐 ...