首先,在看这篇文章前,你需要了解缓存是干嘛的?
缓存
众所周知,程序运行时,数据一般存在内存或磁盘里,而内存中的数据总是可以被更快速的获取。但是内存空间是有限的,大多数人PC的内存可能在4G~16G之间,这意味着你必须要舍弃一部分不频繁被访问的数据,把它们存在磁盘里;而把经常需要被访问的数据存在内存里,这就是缓存的基本思路。
但对于程序(和你)而言,无法预测哪些数据是被经常访问的,所以你只能根据访问数据的历史来推测和统计哪些数据是被经常访问的,并把它存在内存里,如果内存满了就找个最不被经常访问的数据替换掉。这个统计过程和改写内存的策略通常被称为“页面置换算法”,事实上,所有可实现的缓存策略(页面置换算法)都是基于历史访问信息来实现的。科学家经过几十年的努力,发明了许许多多的页面置换算法,比如:FIFO、LFU、LRU、ARC、MQ、GD、GDSF....,它们各有所长,没有孰优孰劣。
缺页数(率):为了评判页面置换算法的优劣,针对访问数据的次数n,和数据未被缓存命中的次数(缺页数)p,缺页率=p/n。显然,缺页率越小,页面置换算法越优秀。
正事
本文基于哈希表的内存管理结构,简单地实现线程安全的缓存算法(LFU, LRU, ARC, MQ, GD, GDSF):
首先看API:(cache.h)
1 #pragma once
2
3 #include <sys/time.h>
4 #ifdef __cplusplus
5 extern "C" {
6 #endif
7
8 typedef struct _cache *cache_t, _cache; /// 定义缓存数据结构
9 typedef struct _cache_ele cache_pair; /// 缓存数据单元
10 typedef struct _cache_ret { /// 缓存调用时的返回结果
11 long cost; /// 数据长度
12 const char*cache; /// 数据地址
13 }cache_ret;
14 /**
15 * @API
16 * create, delete, search, read
17 */
18 cache_t new_cache (unsigned capacity, cache_ret(*model)(cache_t, const char*)); /// 新的缓存
19 void del_cache (cache_t cache); /// 删除缓存
20 unsigned cache_page_fault (cache_t cache); /// 获取缺页数
21 cache_ret read_cache (cache_t cache, const char*filename); /// 读取缓存
22
23 /**
24 * @Cache_Algorithm_Model
25 * cache_ret(*)(cache_t, const char*)
26 */
27 cache_ret LRU (cache_t cache, const char*request); /// 缓存策略
28 cache_ret LFU (cache_t cache, const char*request);
29 cache_ret ARC (cache_t cache, const char*request);
30 cache_ret MQ (cache_t cache, const char*request);
31 cache_ret GD (cache_t cache, const char*request);
32 cache_ret GDSF(cache_t cache, const char*request);
33
34 #ifdef __cplusplus
35 }
36 #endif
数据结构:
缓存:
struct _cache_ele { /// 数据单元
char *key, *file_cache; /// 键值、数据
long cost; /// 代价(长度)
struct timeval pre_t; /// 上次访问时间
unsigned int cnt; /// 访问次数
struct _cache_ele *nxt, *pre;
};
struct _cache {
cache_pair table[HASHTABELSIZE], *first_table; /// 哈希表,first_table根据需要生成
cache_ret (*f)(cache_t, const char *); /// 页面置换策略
pthread_mutex_t mutex; /// 线程安全的锁
unsigned int capacity, _cur, first_cur, page_fault; /// 容量、当前量、ft当前量、缺页数
};/// *cache_t
缓存策略实现:
缓存策略实际上是一个选择问题,如果缓存没有满,那么显然可以直接把新请求的数据直接读到缓存中,如果满了,则按照策略选一个数据替换掉。
(cache.c完整代码)
---版权:代码遵从MIT协议开源,可以按需使用。(地址:cache.c)
---转载需标明出处,侵权必究
---作者:RhythmLian: https://github.com/Rhythmicc