LRU アルゴリズムの詳細な説明: 最も最近使用されていないキャッシュの置換戦略
導入
コンピューター サイエンスでは、キャッシュはデータ アクセスを高速化するために使用される一般的な最適化手法です。キャッシュとは、必要なときにすぐにアクセスできるように、最近使用したデータのコピーを保存する高速メモリを指します。コンピュータ システムでは、最も一般的なタイプのキャッシュの 1 つはメモリ キャッシュで、これにはページ キャッシュとオブジェクト キャッシュが含まれます。キャッシュでは、LRU (Least Recent Used) というキャッシュ置換戦略が一般的ですが、この記事では LRU アルゴリズムの原理と実装について詳しく紹介します。
LRU アルゴリズムの原理
LRU アルゴリズムは、最も最近使用されていない原則という単純な概念に基づいています。このアルゴリズムでは、最近使用されたデータは将来も使用される可能性が高く、長期間使用されなかったデータは将来も未使用のままになる可能性が高いと考えられます。したがって、LRU アルゴリズムはキャッシュ置換戦略を使用します。つまり、長期間アクセスされていないデータがキャッシュから削除されます。
LRU アルゴリズムの中心的な考え方は、順序付けられたデータ アクセス履歴を維持することです。データにアクセスすると、そのデータは履歴の先頭に移動しますが、長期間アクセスされていないデータは履歴の最後に表示されます。キャッシュ スペースが不十分な場合、LRU アルゴリズムは履歴レコードの最後にあるデータを選択して置き換えます。
LRUアルゴリズムの実装方法
二重リンクリストを使用する
LRU アルゴリズムを実装する一般的な方法は、二重リンク リストを使用することです。リンクされたリストの先頭は最近アクセスされたデータを表し、末尾は最も長い間アクセスされていないデータを表します。データにアクセスするたびに、そのデータをリンクされたリストの先頭に移動できます。新しいデータを挿入したいがキャッシュがいっぱいである場合は、リンクされたリストの最後にあるデータを削除することを選択できます。
以下は、二重リンク リストを使用して LRU アルゴリズムを実装するための擬似コードです。
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.cache = {}
self.head = DoubleLinkedListNode()
self.tail = DoubleLinkedListNode()
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key: int) -> int:
if key in self.cache:
node = self.cache[key]
self._move_to_head(node)
return node.value
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
node.value = value
self._move_to_head(node)
else:
node = DoubleLinkedListNode(key, value)
self.cache[key] = node
self._add_to_head(node)
if len(self.cache) > self.capacity:
tail_node = self._remove_tail()
del self.cache[tail_node.key]
def _move_to_head(self, node: DoubleLinkedListNode) -> None:
self._remove_node(node)
self._add_to_head(node)
def _add_to_head(self, node: DoubleLinkedListNode) -> None:
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def _remove_node(self, node: DoubleLinkedListNode) -> None:
node.prev.next = node.next
node.next.prev = node.prev
def _remove_tail(self) -> DoubleLinkedListNode:
tail_node = self.tail.prev
self._remove_node(tail_node)
return tail_node
class DoubleLinkedListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.prev = None
self.next = None
ハッシュテーブルとキューの使用
LRU アルゴリズムを実装するもう 1 つの方法は、ハッシュ テーブルとキューを使用することです。ハッシュ テーブルは特定のキーを持つノードをすばやく見つけることができ、キューはデータがアクセスされる順序を維持できます。
以下は、ハッシュ テーブルとキューを使用して LRU アルゴリズムを実装するための擬似コードです。
from collections import deque
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.cache = {}
self.queue = deque()
def get(self, key: int) -> int:
if key in self.cache:
self.queue.remove(key)
self.queue.append(key)
return self.cache[key]
else:
return -1
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.queue.remove(key)
elif len(self.cache) >= self.capacity:
evicted_key = self.queue.popleft()
del self.cache[evicted_key]
self.cache[key] = value
self.queue.append(key)
結論は
LRU アルゴリズムは、最近使用されたデータの原則に基づいてデータを削除する、一般的に使用されるキャッシュ置換戦略です。LRU アルゴリズムは、順序付けられたアクセス履歴を維持することにより、キャッシュ ヒット率を効果的に向上させることができます。この記事では、LRU アルゴリズムの原理と、二重リンク リストの使用とハッシュ テーブルとキューの使用という 2 つの一般的な実装方法を紹介します。読者は、特定のアプリケーション シナリオに基づいて独自の方法を選択して LRU キャッシュを実装できます。