Наименее недавно использованный кэш с использованием C ++

StackOverflow https://stackoverflow.com/questions/3639744

Вопрос

Я пытаюсь реализовать кеш LRU, используя C ++. Я хотел бы знать, какой лучший дизайн для их реализации. Я знаю, что LRU должен предоставить находку (), добавить элемент и удалить элемент. Удаление должно удалить элемент LRU. Что лучше всего реализовать для реализации этого для ex: если я использую карту с элементом в качестве значения и времени и времени, в качестве ключа, который я могу найти в O (logn), вставка - это o (n), удаление o (logn).

Это было полезно?

Решение

Один из основных проблем с кэшами LRU состоит в том, что есть небольшие операции «Const», большинство изменит основное представление (если только потому, что они поднимают доступ к элементу).

Это, конечно, очень неудобно, потому что это означает, что это не традиционный контейнер STL, и, следовательно, любая идея экспонатов итераторов вполне сложна: когда итератор разветвлен, это доступ, который должен изменить список, который мы итерация на ... о мой.

И есть рассмотрение выступлений, как в течение срока скорости, так и потребления памяти.

К сожалению, но вам понадобится какой-то способ организовать ваши данные в очереди (LRU) (с возможностью удаления элементов с середины), и это означает, что ваши элементы должны быть независимыми друг от друга. А. std::list Подходит, конечно, но это больше, чем нужно. Здесь достаточно связанный список, так как вам не нужно повторять список обратно (вы просто хотите, чтобы очередь, ведь).

Однако один важный недостаток в том, что это их плохое местоположение ссылки, если вам нужна больше скорости, вам понадобится предоставить свой собственный пользовательский (пул?) Распределитель для узлов, чтобы они были настолько близко друг к другу. Это также немного облегчает фрагментацию кучи.

Далее вы, очевидно, нуждаетесь в структуре индекса (для кэш-бита). Наиболее естественным является обращение к хэш-карте. std::tr1::unordered_map, std::unordered_map или boost::unordered_map Обычно это хорошее качество реализации, некоторые должны быть доступны для вас. Они также распределяют дополнительные узлы для обработки хэш-столкновения, вы можете предпочесть другие виды хэш-карт, проверить Статья Википедии На эту тему и прочитать о характеристиках различной техники реализации.

Продолжение, есть (очевидная) резьба поддержки. Если вам не нужна поддержка потоков, то все в порядке, если вы это сделаете, это немного сложнее:

  • Как я уже сказал, там мало const Операция по такой структуре, таким образом, таким образом вам не нужно дифференцировать доступ к чтению / записи
  • Внутренняя блокировка в порядке, но вы можете обнаружить, что он не играет приятно с вашим использованием. Проблема с внутренней блокировкой заключается в том, что она не поддерживает концепцию «транзакции», поскольку она отказывается от блокировки между каждым вызовом. Если это ваше дело, преобразуйте свой объект в Mutex и предоставьте std::unique_ptr<Lock> lock() Метод (в отладке, вы можете утверждать, как замок принимается в точке записи каждого метода)
  • Есть (в заблокировании стратегий) вопрос о возвращении, т. Е. Возможность «мочить» мьютекс из одной и той же нити, проверять Boost.Thread для получения дополнительной информации о различных блоках и доступных мьютреном

Наконец, есть проблема отчетности об ошибках. Поскольку ожидается, что кэш может быть не в состоянии извлечь данные, которые вы передаете, я бы рассмотрел, используя исключение «плохой вкус». Рассмотрим либо указатели (Value*) или Boost.optional (boost::optional<Value&>). Я бы предпочел повысить .optional, потому что его семантика ясна.

Другие советы

Лучший способ реализации LRU - это использовать комбинацию STD :: Список и STDEXT :: HASH_MAP (хотите использовать только std, а затем std :: map).

  • Храните данные в списке так, чтобы наименее недавно использованные в последнее время и используйте карту, чтобы указать на элементы списка.
  • Для «get» используйте карту, чтобы получить список ADDR и извлечь данные и переместить текущий узел в
    Сначала (поскольку это использовалось сейчас) и обновить карту.
  • Для «вставки» удалите последний элемент из списка и добавьте новые данные на фронт и обновите карту.

Это самый быстрый, который вы можете получить, если вы используете HASH_MAP, вы должны практически иметь все операции, выполненные в O (1). При использовании STD :: MAP следует принимать O (logn) во всех случаях.

Очень хорошая реализация доступна здесь

Этот статья Описывает пару реализаций C ++ LRU Cache (один с использованием STL, один с использованием boost::bimap).

Когда вы говорите приоритет, я думаю,куча«Что естественно приводит к повышение ключей а также delete-min..

Я бы не сделал бы кэш видимым на внешний мир вообще, если бы я мог избежать этого. У меня просто есть коллекция (из чего-то) и незаметно обрабатывать кэширование, добавляя и удаление предметов по мере необходимости, но внешний интерфейс будет именно то, что из базовой коллекции.

Что касается реализации, куча, вероятно, является наиболее очевидной. Он имеет сложности примерно похожи на карту, но вместо того, чтобы построить дерево из связанных узлов, он организует предметы в массиве, а «Ссылки» непливые на основе индексов массива. Это увеличивает плотность хранения вашего кэша а также Улучшает местность в «реальном» (физическом) кэше процессора.

Предлагаю куча и может быть Heap Fibonacci

Я бы пошел с нормальной кучей в C ++.

С std :: make_heap (гарантировано стандартом, чтобы быть о (N)), std :: pop_heap, а std :: push_heap в #include, реализуя это будет абсолютно торт. Вам нужно только беспокоиться о повышении ключа.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top