Как кэш LRU будет работать для структуры данных trie?

Допустим, у меня есть trie/prefix trie с общим лимитом в 10 узлов. Я ограничиваюсь 10 узлами, чтобы имитировать превышение памяти. (Если я не могу загрузить все дерево в память, у меня всего - 10 узлов, хранящихся на диске.

Теперь я вставляю в дерево новую строку, которая приведет к тому, что дерево превысит ограничение в 10 узлов, поэтому теперь пришло время для кэша LRU удалить из дерева узел, к которому последний раз обращались.

Допустим, дерево содержит слова привет, помогите, привет, а узел LRU — «h». Это означало бы, что мне нужно удалить «h» из дерева, что в данном случае удалит все дерево. Моя путаница заключается в обновлении самого кеша, чтобы удалить все дочерние элементы. Как это работает в данном случае?

Я предполагаю, что в кеше есть такие узлы, как «h», «he», «hel», «help» и т. д. Если я удалю узел «h», я предполагаю, что мне нужно удалить все в кеше с префиксом «h»? Все мое предположение кажется действительно неэффективным.


person John Lippson    schedule 05.07.2019    source источник
comment
Зачем вам попытка с ограничением на количество узлов, которая автоматически удаляет старые узлы? Что это означает? Ясно, что удаление h не имеет смысла — как h может быть даже узлом LRU, если он является корнем вашего дерева? Как он может использоваться менее недавно, чем его ребенок?   -  person Erwin Bolwidt    schedule 05.07.2019
comment
Ограничение в 10 узлов было бы гипотетической симуляцией для имитации ограничения количества узлов, которые я могу загрузить в память одновременно. Если я вставлю «привет» в префиксную строку, я вставлю «h», затем «he», затем «hel», «hell», «hello». В этом случае самым новым узлом вставки будет «o» из префикса «hello», а самым старым будет «h», не так ли?   -  person John Lippson    schedule 05.07.2019
comment
LRU используется наименее недавно; поскольку вы ничего не извлекли из теста, я бы сказал, что все они одного возраста. Кажется не очень разумным удалять что-либо, кроме листового узла, поскольку удаление любого нелистового узла автоматически удалит и всех его дочерних элементов. Но удалять отдельные узлы кажется не очень разумным - только строку, которую вы вставили в дерево; но trie обычно не сохраняет исходные строки, которые были вставлены, поэтому их будет трудно удалить, если вы не измените структуру данных (возможно, подсчитывая, сколько строк использовал каждый узел)   -  person Erwin Bolwidt    schedule 05.07.2019
comment
Есть ли более разумный способ чтения/записи фрагментов префикса на диск и обратно в память по мере достижения предела памяти? Я предполагал, что некоторая политика LRU, основанная на использовании памяти, будет работать, но фактическое вытеснение узлов меня действительно сбивает с толку. Я не думал, что все они одного возраста, потому что думал, что кеш LRU обычно представляется в виде двусвязного списка.   -  person John Lippson    schedule 05.07.2019


Ответы (1)


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

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

Короче говоря, две упомянутые вами вещи (узлы trie в java и политики кэширования LRU) слишком далеки друг от друга (одна — программирование очень, очень низкого уровня, другая — высокого уровня). Вот почему мы редко, если вообще когда-либо, рассматриваем их взаимодействие.
Если вы реализуете Trie в Java, ваша задача состоит в том, чтобы убедиться, что он хорошо работает во всех ситуациях, что он хорошо спроектирован, чтобы обслуживание было проще (возможно, ), что он удобочитаем, чтобы другие программисты могли когда-нибудь над ним поработать. В конце концов, если он по-прежнему работает слишком медленно, вы можете попытаться оптимизировать его (после определения узких мест, но никогда раньше).
Но если вы хотите связать свою попытку с совпадением кэша и политиками замены, придется прочитать перевод вашей реализации в байт-код (сделанный JVM).

PS: в своем посте вы говорите об имитации превышения памяти. Для программы такого нет. Когда кеш заполнен, мы заполняем основную память. Когда основная память заполнена, операционные системы обычно резервируют часть жесткого диска для выполнения роли основной памяти (мы называем это подкачкой, и когда это происходит, компьютер практически зависает). Когда своп заполнен, программы вылетают. Все.
В «разуме» программы операционная система выделяет ей совершенно гигантские объемы памяти (виртуальной, но для программы все равно, что реальной), которые никогда не заполнятся. Сама программа не «осознает» способ управления памятью и объем оставшейся памяти по многим веским причинам (безопасность, гарантия того, что все программы будут иметь справедливую долю ресурсов...)

person m.raynal    schedule 05.07.2019
comment
Спасибо за ответ, многое проясняется. Я предполагаю, что моя главная путаница заключается в обмене значениями на диск и наоборот. Допустим, у меня 2 МБ оперативной памяти и 100 ТБ на диске. Как загрузить и выгрузить с диска? - person John Lippson; 05.07.2019
comment
Обычно мы называем эту операцию сериализацией (и десериализацией). В java объекты, которые реализуют сериализуемый интерфейс, могут быть сохранены/загружены из/в файл. - person m.raynal; 08.07.2019
comment
И если вы хотите загрузить с диска некоторые данные, которые больше, чем ОЗУ, обычно у вас есть 2 разумных варианта: 1 — получить больше ОЗУ 2 — использовать меньше ОЗУ. Вы всегда можете загрузить/сохранить на диск, но это займет много времени (десятки, если не сотни миллионов циклов ЦП) в любое время, когда вы захотите выполнить такую ​​операцию. Принцип вообще в том, чтобы хранить все данные в ОЗУ, потом с ними работать. - person m.raynal; 08.07.2019