この記事を読む
1.キャッシュとは
2.LRUキャッシュとは
3.LRUキャッシュ設計のアイデア
4.LRUキャッシュインターフェイス
キャッシュとは
実際、キャッシングのアイデアは人生のいたるところに見られます。たとえば、私たちの本棚にはさまざまな本がたくさんありますが、最近はデータ構造のコースを征服することに集中したいと思います。便宜上、最初にデータ構造に関連する本を作成します。本棚から降ろして机の上に置くと、棚に行かなくても早く本を受け取ることができます。この例では、机がキャッシュの役割を果たしています。 。
コンピュータでは、ディスクへのアクセス効率がメモリへのアクセス効率よりもはるかに低いことがわかっています。次の図に示すように、ディスクへのアクセス効率ははるかに低く、約10Wです。
非常に有名な28の法則があることを私たちは知っています。実際、コンピューターの世界では、Webサイトへのアクセスの特性など、この法律に従う多くの状況があります。ビジネスへのアクセスの80%はデータの20%に集中しています。次に、考えるのは自然です。ほとんどのビジネスアクセスはデータのごく一部に集中しているため、合理的なデータ構造を設計してこのデータのごく一部をメモリに保存すると、システムのパフォーマンスが大幅に向上しますか?これがキャッシュです。
上記からわかるように、キャッシュは実際の世界であろうとコンピューターの世界であろうと、次の2つの特徴があります。
-
システム効率を改善し、
- デスクの容量が本棚の容量よりも少なく、メモリの容量がハードディスクの容量よりも少ないなど、容量が外部ストレージの容量よりも少ない
LRUキャッシュとは
メモリが限られているため、キャッシュは常にいっぱいです。キャッシュがいっぱいになると、新しいデータをキャッシュに追加する必要があります。どうすればよいですか。古いデータをキャッシュから削除して新しいデータ用のスペースを確保する以外に方法はありません。次に、元のキャッシュからどのデータを削除しますか?これにはキャッシュ置換戦略が含まれ、LRUはキャッシュ戦略です。
LRUは、「最近使用されていない」の略で、「最近使用されていない」という意味です。このアルゴリズムは、プログラムの局所性の原則に基づいており、古いデータを削除するための戦略は、現在の日付から最も長い時間アクセスされていないデータを削除することです。
例:これで、5つのデータを格納できるキャッシュができました。最初に、以下に示すよう
に、キャッシュに3つのデータA-> B-> Cがあります。次に2つのデータD-> Eをキャッシュに挿入します。、この時点でキャッシュがいっぱいです。次のようになります。これで
、データAにアクセスできるようになり、Aが最後にアクセスされたデータになり、Bが最も長い間アクセスされなかったデータ
になりました。キャッシュがいっぱいであるため、キャッシュに挿入する別のデータFがあります。古いデータをキャッシュから削除する必要があります。このとき、キャッシュ内のデータBは、現在から最も長い時間アクセスされていないデータです。LRUアルゴリズムに従って、データBを削除します。現在のBの位置にFを挿入します。
このとき、データCは、現在から最も長い時間アクセスされていないデータになるため、別のデータが来た場合は、Cの現在位置に挿入する必要があります。
LRUキャッシュの設計
LRUキャッシュを理解するという考え方は実際には非常に単純ですが、LRUキャッシュを自分で設計することはそれほど単純ではありません。このセクションでは、主にLRUキャッシュの設計の考え方について説明します。
まず、前回の分析から、LRUキャッシュの主な操作は挿入、削除、検索であることがわかります。したがって、二重リンクリストを使用してキャッシュを維持することを検討できます。リンクリストは、キャッシュされたデータをアクセス時間ごとに新しいものから古いものへと並べます。
二重リンクリストのキャッシュにデータを格納するだけの場合、キャッシュからデータを見つける必要があるときは、リンクリストをトラバースする必要があり、時間の複雑さはO(n)です。このような設計は、比較的非効率的なアプローチです。したがって、二重にリンクされたリストにデータを保持することに加えて、ハッシュテーブルにもデータを保持します。データへのハッシュテーブルアクセスの時間の複雑さはO(1)です。
上記の設計によれば、LRUキャッシュには二重リンクリストとハッシュテーブルが含まれます。二重リンクリストとハッシュテーブルのノードはキャッシュ内のキャッシュユニットを表すため、キャッシュユニットのデータ構造を次のように定義できます。
//LRU缓存的缓存单元
typedef struct cacheEntryS
{
char key; //数据的key
char data; // 数据的data
struct cacheEntryS *hashListPrev; //指向哈希链表的前一个元素
struct cacheEntryS *hashListNext; //指向哈希链表的后一个元素
struct cacheEntryS *lruListPrev; //指向链表的前一个元素
struct cacheEntryS *lruListNext; //指向链表后一个元素
}cacheEntryS;
LRUキャッシュのデータ構造は次のとおりです。
//定义LRU缓存
typedef struct LRUCacheS
{
int cacheCapacity; //缓存的容量
cacheEntryS **hashMap; //缓存的哈希表
cacheEntryS *lruListHead;//缓存的双向链表表头
cacheEntryS *lruListTail;//缓存的双向链表表尾
int lruListSize; //缓存的双向链表节点个数
}LRUCacheS;
多くのコードが目がくらむように見える場合は、下の構造図をより完全に理解できるはずです。下の図で示されていないポインターはNULLであることに注意してください。
LRUキャッシュインターフェイスの設計
以前の分析を通じて、LRUキャッシュでの最も重要な操作が、データの作成、破棄、挿入、およびデータの検索にすぎないことを確認することは難しくありません。これらのインターフェースを以下に示します。具体的な実装については、ここでは分析しません。後でそれらを一つずつ分析する機会があります。
/*********************************************
函数名:LRUCacheCreate
功能:创建LRU缓存
输入参数:capacity,缓存的数据容量
输出参数:lruCache,指向新建缓存的指针
针返回值:0---成功 -1---失败
*********************************************/
int LRUCacheCreate(int capacity, void **lruCache);
/*********************************************
函数名:LRUCacheDestory
功能:销毁LRU缓存
输入参数:lruCache指向新建缓存的指针
输出参数:无
针返回值:0---成功 -1---失败
*********************************************/
int LRUCacheDestory(void *lruCache);
/*********************************************
函数名:LRUCacheSet
功能:将数据插入到LRU缓存中
输入参数:key:数据索引 data:数据内容
输出参数:无
针返回值:0---成功 -1---失败
*********************************************/
int LRUCacheSet(void *lruCache, char key, char data);
/*********************************************
函数名:LRUCacheGet
功能:将数据插入到LRU缓存中
输入参数:key:数据索引
输出参数:无
针返回值:缓存中存在key对应的data,返回
缓存中不存在key对应的data,返回'\0'
*********************************************/
int LRUCacheGet(void *lruCache, char key);
推奨読書:
【メリット】自分で集めたオンラインプレミアムコース動画の共有(
パート1 )【アグリーメントフォレスト】郵便局と郵便局(インターネットプロトコルの概要)
[データ構造とアルゴリズム]ビットソートのわかりやすい説明
[C ++メモ] C ++ 11同時プログラミング(1)スレッドジャーニーの開始
[C ++メモ] C / C ++ポインターを使用するための一般的な落とし穴
[C ++メモ]静的および動的ライブラリ詳細な説明(上)
コーディング
コードファーマーには、テクノロジーを簡単にするためのわかりやすい技術記事を提供する正しい方法があります。