Алгоритм кэширования часов

Я хочу понять принципы политики замены кэша часов.

Что происходит, когда он начинает работать? У нас например размер кеша = 5. Итак, сначала мы добавляем 5 случайных объектов в кеш. Прав ли я, когда думаю, что все эти объекты поначалу будут иметь clockbit = 0? Затем, когда появится шестой объект, мы должны найти для него место. Должны ли мы пытаться найти тот же объект в кеше без часовой стрелки (только ifInCache(comingObject))? Что произойдет, если такого объекта в кеше нет? Где стартовая позиция для стрелки часов?

Я прочитал много статей и просто хочу понять основные вопросы о часах.


person golgofa    schedule 12.04.2012    source источник
comment
Большое спасибо, все ответы были очень полезными. Мой английский не очень хорош, поэтому я могу понять всю грамматику в английских статьях, но иногда не могу понять весь смысл.   -  person golgofa    schedule 12.04.2012
comment
И еще несколько вопросов. Я использую Clock для замены кеша Clock-Pro. Основная статья слишком сложная и реальная реализация только я видел в ядре linux. Кто-нибудь пробовал что-то на Java? Больше всего проблем было со специальными терминами: т.е. я не видел объяснения, что такое резидентная и нерезидентная страница. И нигде не видел простых примеров работы.   -  person golgofa    schedule 12.04.2012


Ответы (2)


Мне кажется, вы сами себе все усложняете. Причина, по которой люди используют этот подход, заключается в том, что он ОЧЕНЬ прост.

Это позволит избежать замены записи, которая недавно использовалась.

private final Object[] objects = new Object[5];
private final boolean[] referenced = new boolean[objects.length];
private int clock = 0;

public Object getOrCache(Object obj) {
    for (int i = 0, objectsLength = objects.length; i < objectsLength; i++) {
        Object o = objects[i];
        if (obj.equals(o)) {
            referenced[i] = true;
            return obj;
        }
    }
    while(referenced[clock]) {
        referenced[clock] = false;
        incrClock();
    }
    objects[clock] = obj;
    incrClock();
    return obj;
}

private void incrClock() {
    if (++clock >= objects.length)
        clock = 0;
}

Это не беспокоит.

private final Object[] objects= new Object[5];
private int clock = 0;

public Object getOrCache(Object obj) {
   for(Object o: objects) 
       if (obj.equals(o))
           return obj;
   objects[clock++ % objects.length] = obj;
   return obj;
}
person Peter Lawrey    schedule 12.04.2012
comment
Разве не должен быть ссылочный бит, который проверяется, и объект будет заменен только в том случае, если его ссылочный бит равен 0? Таким образом, вы будете увеличивать clock до тех пор, пока не найдете запись с referenced = 0 (которая может быть первым элементом, если на все ссылаются, и, таким образом, первый элемент получит свой ссылочный бит, установленный в 0 в первом раунде). - person Thomas; 12.04.2012
comment
Вы можете сделать это, чтобы сделать псевдо недавно использованный. Я буду редактировать. - person Peter Lawrey; 12.04.2012
comment
Эта реализация, насколько я понимаю, для чего-то вроде простого FIFO. И я должен реализовать эталонные биты, верно? - person golgofa; 12.04.2012
comment
@golgofa Я также добавил справочные биты. - person Peter Lawrey; 12.04.2012

Прав ли я, когда думаю, что все эти объекты поначалу будут иметь clockbit = 0?

Если они не указаны, то да.

Должны ли мы пытаться найти тот же объект в кеше без часовой стрелки (только ifInCache(comingObject))?

Да, вы должны проверить, находится ли объект уже в кеше. В этом случае опорный бит (тактовый бит) будет установлен в 1.

Что произойдет, если такого объекта в кеше нет? Где стартовая позиция для стрелки часов?

Если объект еще не находится в кеше, вы проверяете объект по стрелке часов. Позиция руки будет последней позицией в кеше, если она еще не заполнена, и в противном случае останется неизменной между двумя поисками в кеше (она будет увеличена самими поисками).

Пример (размер кеша = 5):

  • добавить A -> рука на 0 до и на 1 после
  • добавить B -> рука на 1 до и на 2 после
  • добавить C -> рука на 2 до и на 3 после
  • добавить D -> рука на 3 до и на 4 после
  • добавить E -> рука на 4 до и на 0 после
  • добавить F -> рука в 0, проверить указанный бит A, если он равен 0, заменить и увеличить руку, в противном случае увеличить только руку -> после этого рука будет в 1

Обратите внимание, что если у всех объектов бит ссылки установлен на 1, объект в руке будет заменен, так как после проверки объекта его бит ссылки установлен на 0, и, таким образом, при второй проверке объекта бит будет равен 0.

Изменить:

Вот расширенная/скорректированная версия кода @PeterLawrey:

private final Object[] objects= new Object[5]; 
private final boolean[] referenced = new boolean[5]; //boolean for simplicity
private int clock = 0;

public Object getOrCache(Object obj) {
   for(int i = 0; i < objects.length; ++i) {
     if (obj.equals(objects[i])) {
       referenced[i] = true; //object has been referenced, note that this is for simplicity and could be optimized
       return obj;
     }
   }

   //loop over the entries until there is a non-referenced one
   //reference flags are removed in the process
   while( referenced[clock] ) {
     referenced[clock] = false;
     clock = (clock + 1) % objects.length; //for clarity
   }

   //replace the object at the current clock position and increment clock
   objects[clock] = obj;
   referenced[clock] = true;
   clock = (clock + 1) % objects.length; //for clarity

   return obj;
}
person Thomas    schedule 12.04.2012
comment
Вы увеличиваете часы двумя разными способами, я подозреваю, что первый неверен. ;) - person Peter Lawrey; 12.04.2012
comment
Массивы не имеют метода indexOf. - person Peter Lawrey; 12.04.2012
comment
@PeterLawrey, вы правы, я это исправлю :) - Я так привык работать с коллекциями, а не с массивами, что совершенно забыл, что у нас здесь есть массив. - person Thomas; 12.04.2012