関連コンテンツをキャッシュする

1.キャッシングの概要

キャッシュとは高速データを交換できるメモリのことです。メモリの前にCPUとデータ交換するため、速度が非常に速くなります。

少数のデータコピーにメモリのみをキャッシュするため、CPUキャッシュにデータを検索するときに、状況が見つからない場合(データがキャッシュメモリからコピーされないため)、CPUは引き続きデータを探すのメモリ。システムの速度は低下しますが、CPUはデータをキャッシュにコピーして、次回メモリから取得されないようにします。時間の経過とともに、最も頻繁にアクセスされるデータは静的ではありません。つまり、今は頻繁にアクセスされていないデータに頻繁にアクセスする必要があります。現在最も頻繁にアクセスされるデータは頻繁でないため、データはキャッシュ内のデータが最も頻繁にアクセスされるように、特定のアルゴリズムに従ってキャッシュ内のデータを頻繁に置き換える必要があります。

2.動作原理

キャッシュの動作原理は、CPUがデータの一部を読み取りたい場合、最初にCPUキャッシュからデータを検索し、次にそれをすぐに読み取り、見つかった場合はCPUに送信します。見つかった場合はCPUに送信します。比較的遅いメモリから読み取り、CPUに送信します。処理すると同時にデータが配置されているデータブロックをキャッシュに転送して、将来的にデータのブロック全体をキャッシュから読み取ることができるようにします。メモリを呼び出さずに。CPU読み取りキャッシュのヒット率を非常に高くするのはこの読み取りメカニズムです(ほとんどのCPUは約90%に達する可能性があります)。つまり、次回CPUによって読み取られるデータの90%CPUキャッシュにあり、約10のみです。 %メモリから読み取る必要がありますこれにより、CPUがメモリを直接読み取る時間を大幅に節約でき、CPUがデータを読み取るときに基本的に待機する必要がなくなります一般に、CPUがデータを読み取る順序は最初にキャッシュ、次にメモリです。

 

3、キャッシュ置換アルゴリズム

キャッシュアルゴリズムは、キャッシュシステム内のどのデータを削除するかを決定するために使用される命令の詳細なリストです。

一般的なタイプには、LFU、LRU、ARC、FIFO、MRUが含まれます。

最も使用頻度の低いアルゴリズム(LFU):

このキャッシュアルゴリズムは、カウンターを使用して、エントリがアクセスされる頻度を記録します。LFUキャッシュアルゴリズムを使用すると、アクセス数が最も少ないエントリが最初に削除されます。この方法は、最初の高いアクセス率の後で長期間アクセスされていないエントリキャッシュを処理できないため、あまり使用されません。

最近使用されていないアルゴリズム(LRU):

このキャッシュアルゴリズムは、最近使用されたエントリをキャッシュの先頭近くに保存します。新しいエントリにアクセスすると、LRUはそのエントリをキャッシュの先頭に配置します。キャッシュが制限に達すると、以前にアクセスされたエントリがキャッシュの下部から削除されます。ここでは高価なアルゴリズムが使用されており、エントリがいつアクセスされたかを正確に示すために「年齢位置」を記録する必要があります。さらに、LRUキャッシュアルゴリズムがエントリを削除すると、「年齢位置」は他のエントリとともに変化します。

アダプティブキャッシュ置換アルゴリズム(ARC):

IBM Almaden Research Centerで開発されたこのキャッシュアルゴリズムは、LFUとLRUを同時に追跡し、キャッシュエントリを削除して、使用可能なキャッシュを最大限に活用します。

先入れ先出しアルゴリズム(FIFO):

FIFOは、English First In First Outの略です。先入れ先出しデータバッファです。通常のメモリとの違いは、外部の読み取りおよび書き込みアドレス行がないことです。使用は非常に簡単です。ただし、デメリットは、データを順次書き込むことしかできないことです。、順次読み取りデータ、データアドレスは内部読み取りおよび書き込みポインタによって自動的に追加されて完了し、通常のメモリのようにアドレス行によって指定されたアドレスに対して読み取りまたは書き込みを行うことはできません。 。

最近使用されたアルゴリズム(MRU):

このキャッシュアルゴリズムは、最初に最近使用されたエントリを削除します。MRUアルゴリズムは、アイテムが長いほどアクセスしやすいという状況に対処するのに適しています。

 

インタビューにより、LRUアルゴリズムを手作業で破ることがあります。以下は、参考のために、以前に見たアルゴリズムの実装です。

二重リンクリスト+ハッシュテーブルの実現原理:

キャッシュのすべての位置は二重リンクリストで接続されています。位置がヒットすると、リンクリストの方向を調整することで位置がリンクリストの先頭に調整され、新しく追加されたキャッシュが直接追加されます。リンクリストの先頭に。このように、複数のキャッシュ操作の後、最近ヒットしたものはリンクリストの先頭に移動し、ヒットしなかったものはリンクリストの末尾に移動します。リンクリストの終わりは最も最近使用されていないキャッシュ。コンテンツを置き換える必要がある場合、リンクリストの最後の位置が最もヒットの少ない位置であり、リンクリストの最後の部分を削除するだけで済みます。

元のリンク:https://blog.csdn.net/beiyeqingteng/article/details/7010411

 

public class LRUCache {
	
	private int cacheSize;
	private Hashtable<Object, Entry> nodes;//缓存容器
	private int currentSize;
	private Entry first;//链表头
	private Entry last;//链表尾
	
	public LRUCache(int i) {
		currentSize = 0;
		cacheSize = i;
		nodes = new Hashtable<Object, Entry>(i);//缓存容器
	}
	
	/**
	 * 获取缓存中对象,并把它放在最前面
	 */
	public Entry get(Object key) {
		Entry node = nodes.get(key);
		if (node != null) {
			moveToHead(node);
			return node;
		} else {
			return null;
		}
	}
	
	/**
	 * 添加 entry到hashtable, 并把entry 
	 */
	public void put(Object key, Object value) {
		//先查看hashtable是否存在该entry, 如果存在,则只更新其value
		Entry node = nodes.get(key);
		
		if (node == null) {
			//缓存容器是否已经超过大小.
			if (currentSize >= cacheSize) {
				nodes.remove(last.key);
				removeLast();
			} else {
				currentSize++;
			}			
			node = new Entry();
		}
		node.value = value;
		//将最新使用的节点放到链表头,表示最新使用的.
		moveToHead(node);
		nodes.put(key, node);
	}
 
	/**
	 * 将entry删除, 注意:删除操作只有在cache满了才会被执行
	 */
	public void remove(Object key) {
		Entry node = nodes.get(key);
		//在链表中删除
		if (node != null) {
			if (node.prev != null) {
				node.prev.next = node.next;
			}
			if (node.next != null) {
				node.next.prev = node.prev;
			}
			if (last == node)
				last = node.prev;
			if (first == node)
				first = node.next;
		}
		//在hashtable中删除
		nodes.remove(key);
	}
 
	/**
	 * 删除链表尾部节点,即使用最后 使用的entry
	 */
	private void removeLast() {
		//链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
		if (last != null) {
			if (last.prev != null)
				last.prev.next = null;
			else
				first = null;
			last = last.prev;
		}
	}
	
	/**
	 * 移动到链表头,表示这个节点是最新使用过的
	 */
	private void moveToHead(Entry node) {
		if (node == first)
			return;
		if (node.prev != null)
			node.prev.next = node.next;
		if (node.next != null)
			node.next.prev = node.prev;
		if (last == node)
			last = node.prev;
		if (first != null) {
			node.next = first;
			first.prev = node;
		}
		first = node;
		node.prev = null;
		if (last == null)
			last = first;
	}
	/*
	 * 清空缓存
	 */
	public void clear() {
		first = null;
		last = null;
		currentSize = 0;
	}
 
}
 
class Entry {
	Entry prev;//前一节点
	Entry next;//后一节点
	Object value;//值
	Object key;//键
}

 

おすすめ

転載: blog.csdn.net/orzMrXu/article/details/102530625