#ifndef CEPH_LRU_H
#define CEPH_LRU_H
+#include <math.h>
#include <stdint.h>
#include "common/config.h"
-
-
+#include "xlist.h"
class LRUObject {
- private:
- LRUObject *lru_next, *lru_prev;
- bool lru_pinned;
- class LRU *lru;
- class LRUList *lru_list;
-
- public:
- LRUObject() {
- lru_next = lru_prev = NULL;
- lru_list = 0;
- lru_pinned = false;
- lru = 0;
- }
+public:
+ LRUObject() : lru(), lru_link(this), lru_pinned(false) { }
+ ~LRUObject();
// pin/unpin item in cache
- void lru_pin();
+ void lru_pin();
void lru_unpin();
bool lru_is_expireable() const { return !lru_pinned; }
friend class LRU;
- friend class LRUList;
+private:
+ class LRU *lru;
+ xlist<LRUObject *>::item lru_link;
+ bool lru_pinned;
};
+class LRU {
+public:
+ LRU() : num_pinned(0), midpoint(0.6) {}
-class LRUList {
- private:
- LRUObject *head, *tail;
- uint32_t len;
+ uint64_t lru_get_size() const { return lru_get_top()+lru_get_bot()+lru_get_pintail(); }
+ uint64_t lru_get_top() const { return top.size(); }
+ uint64_t lru_get_bot() const{ return bottom.size(); }
+ uint64_t lru_get_pintail() const { return pintail.size(); }
+ uint64_t lru_get_num_pinned() const { return num_pinned; }
- public:
- LRUList() {
- head = tail = 0;
- len = 0;
- }
+ void lru_set_midpoint(double f) { midpoint = fmin(1.0, fmax(0.0, f)); }
- uint32_t get_length() const { return len; }
-
- LRUObject *get_head() {
- return head;
- }
- LRUObject *get_tail() {
- return tail;
- }
-
- void clear() {
- while (len > 0) {
- remove(get_head());
+ void lru_clear() {
+ while (!top.empty()) {
+ lru_remove(top.front());
}
- }
-
- void insert_head(LRUObject *o) {
- o->lru_next = head;
- o->lru_prev = NULL;
- if (head) {
- head->lru_prev = o;
- } else {
- tail = o;
+ while (!bottom.empty()) {
+ lru_remove(bottom.front());
}
- head = o;
- o->lru_list = this;
- len++;
- }
- void insert_tail(LRUObject *o) {
- o->lru_next = NULL;
- o->lru_prev = tail;
- if (tail) {
- tail->lru_next = o;
- } else {
- head = o;
+ while (!pintail.empty()) {
+ lru_remove(pintail.front());
}
- tail = o;
- o->lru_list = this;
- len++;
- }
-
- void remove(LRUObject *o) {
- assert(o->lru_list == this);
- if (o->lru_next)
- o->lru_next->lru_prev = o->lru_prev;
- else
- tail = o->lru_prev;
- if (o->lru_prev)
- o->lru_prev->lru_next = o->lru_next;
- else
- head = o->lru_next;
- o->lru_next = o->lru_prev = NULL;
- o->lru_list = 0;
- assert(len>0);
- len--;
- }
-
-};
-
-
-class LRU {
- protected:
- LRUList lru_top, lru_bot, lru_pintail;
- uint32_t lru_num, lru_num_pinned;
- uint32_t lru_max; // max items
- double lru_midpoint;
-
- friend class LRUObject;
- //friend class MDCache; // hack
-
- public:
- LRU(int max = 0) {
- lru_num = 0;
- lru_num_pinned = 0;
- lru_midpoint = .6;
- lru_max = max;
- }
-
- uint32_t lru_get_size() const { return lru_num; }
- uint32_t lru_get_top() const { return lru_top.get_length(); }
- uint32_t lru_get_bot() const{ return lru_bot.get_length(); }
- uint32_t lru_get_pintail() const { return lru_pintail.get_length(); }
- uint32_t lru_get_max() const { return lru_max; }
- uint32_t lru_get_num_pinned() const { return lru_num_pinned; }
-
- void lru_set_max(uint32_t m) { lru_max = m; }
- void lru_set_midpoint(float f) { lru_midpoint = f; }
-
- void lru_clear() {
- lru_top.clear();
- lru_bot.clear();
- lru_pintail.clear();
- lru_num = 0;
+ assert(num_pinned == 0);
}
// insert at top of lru
void lru_insert_top(LRUObject *o) {
- //assert(!o->lru_in_lru);
- //o->lru_in_lru = true;
assert(!o->lru);
o->lru = this;
- lru_top.insert_head( o );
- lru_num++;
- if (o->lru_pinned) lru_num_pinned++;
- lru_adjust();
+ top.push_front(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
}
// insert at mid point in lru
void lru_insert_mid(LRUObject *o) {
- //assert(!o->lru_in_lru);
- //o->lru_in_lru = true;
assert(!o->lru);
o->lru = this;
- lru_bot.insert_head(o);
- lru_num++;
- if (o->lru_pinned) lru_num_pinned++;
+ bottom.push_front(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
}
// insert at bottom of lru
void lru_insert_bot(LRUObject *o) {
assert(!o->lru);
o->lru = this;
- lru_bot.insert_tail(o);
- lru_num++;
- if (o->lru_pinned) lru_num_pinned++;
+ bottom.push_back(&o->lru_link);
+ if (o->lru_pinned) num_pinned++;
+ adjust();
}
- /*
- // insert at bottom of lru
- void lru_insert_pintail(LRUObject *o) {
- assert(!o->lru);
- o->lru = this;
-
- assert(o->lru_pinned);
-
- lru_pintail.insert_head(o);
- lru_num++;
- lru_num_pinned += o->lru_pinned;
- }
- */
-
-
-
-
- // adjust top/bot balance, as necessary
- void lru_adjust() {
- if (!lru_max) return;
-
- unsigned toplen = lru_top.get_length();
- unsigned topwant = (unsigned)(lru_midpoint * ((double)lru_max - lru_num_pinned));
- while (toplen > 0 &&
- toplen > topwant) {
- // remove from tail of top, stick at head of bot
- // FIXME: this could be way more efficient by moving a whole chain of items.
-
- LRUObject *o = lru_top.get_tail();
- lru_top.remove(o);
- lru_bot.insert_head(o);
- toplen--;
- }
- }
-
-
// remove an item
LRUObject *lru_remove(LRUObject *o) {
- // not in list
- //assert(o->lru_in_lru);
- //if (!o->lru_in_lru) return o; // might have expired and been removed that way.
if (!o->lru) return o;
-
- assert((o->lru_list == &lru_pintail) ||
- (o->lru_list == &lru_top) ||
- (o->lru_list == &lru_bot));
- o->lru_list->remove(o);
-
- lru_num--;
- if (o->lru_pinned) lru_num_pinned--;
- o->lru = 0;
+ auto list = o->lru_link.get_list();
+ assert(list == &top || list == &bottom || list == &pintail);
+ o->lru_link.remove_myself();
+ if (o->lru_pinned) num_pinned--;
+ o->lru = nullptr;
+ adjust();
return o;
}
// touch item -- move to head of lru
bool lru_touch(LRUObject *o) {
- lru_remove(o);
- lru_insert_top(o);
+ if (!o->lru) {
+ lru_insert_top(o);
+ } else {
+ assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ assert(list == &top || list == &bottom || list == &pintail);
+ top.push_front(&o->lru_link);
+ adjust();
+ }
return true;
}
// touch item -- move to midpoint (unless already higher)
bool lru_midtouch(LRUObject *o) {
- if (o->lru_list == &lru_top) return false;
-
- lru_remove(o);
- lru_insert_mid(o);
+ if (!o->lru) {
+ lru_insert_mid(o);
+ } else {
+ assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ assert(list == &top || list == &bottom || list == &pintail);
+ if (list == &top) return false;
+ bottom.push_front(&o->lru_link);
+ adjust();
+ }
return true;
}
// touch item -- move to bottom
bool lru_bottouch(LRUObject *o) {
- lru_remove(o);
- lru_insert_bot(o);
+ if (!o->lru) {
+ lru_insert_bot(o);
+ } else {
+ assert(o->lru == this);
+ auto list = o->lru_link.get_list();
+ assert(list == &top || list == &bottom || list == &pintail);
+ bottom.push_back(&o->lru_link);
+ adjust();
+ }
return true;
}
void lru_touch_entire_pintail() {
// promote entire pintail to the top lru
- while (lru_pintail.get_length() > 0) {
- LRUObject *o = lru_pintail.get_head();
- lru_pintail.remove(o);
- lru_top.insert_tail(o);
+ while (pintail.size() > 0) {
+ top.push_back(&pintail.front()->lru_link);
+ adjust();
}
}
-
// expire -- expire a single item
LRUObject *lru_get_next_expire() {
- LRUObject *p;
-
// look through tail of bot
- while (lru_bot.get_length()) {
- p = lru_bot.get_tail();
+ while (bottom.size()) {
+ LRUObject *p = bottom.back();
if (!p->lru_pinned) return p;
// move to pintail
- lru_bot.remove(p);
- lru_pintail.insert_head(p);
+ pintail.push_front(&p->lru_link);
+ adjust();
}
// ok, try head then
- while (lru_top.get_length()) {
- p = lru_top.get_tail();
+ while (top.size()) {
+ LRUObject *p = top.back();
if (!p->lru_pinned) return p;
// move to pintail
- lru_top.remove(p);
- lru_pintail.insert_head(p);
+ pintail.push_front(&p->lru_link);
+ adjust();
}
// no luck!
return NULL;
}
-
void lru_status() {
- //generic_dout(10) << "lru: " << lru_num << " items, " << lru_top.get_length() << " top, " << lru_bot.get_length() << " bot, " << lru_pintail.get_length() << " pintail" << dendl;
+ //generic_dout(10) << "lru: " << lru_get_size() << " items, " << top.size() << " top, " << bottom.size() << " bot, " << pintail.size() << " pintail" << dendl;
}
+protected:
+ // adjust top/bot balance, as necessary
+ void adjust() {
+ uint64_t toplen = top.size();
+ uint64_t topwant = (midpoint * (double)(lru_get_size() - num_pinned));
+ /* move items from below midpoint (bottom) to top: move midpoint forward */
+ for (uint64_t i = toplen; i < topwant; i++) {
+ top.push_back(&bottom.front()->lru_link);
+ }
+ /* or: move items from above midpoint (top) to bottom: move midpoint backwards */
+ for (uint64_t i = toplen; i > topwant; i--) {
+ bottom.push_front(&top.back()->lru_link);
+ }
+ }
+
+ uint64_t num_pinned;
+ double midpoint;
+
+ friend class LRUObject;
+private:
+ typedef xlist<LRUObject *> LRUList;
+ LRUList top, bottom, pintail;
};
+inline LRUObject::~LRUObject() {
+ if (lru) {
+ lru->lru_remove(this);
+ }
+}
inline void LRUObject::lru_pin() {
if (lru && !lru_pinned) {
- lru->lru_num_pinned++;
- lru->lru_adjust();
+ lru->num_pinned++;
}
lru_pinned = true;
}
inline void LRUObject::lru_unpin() {
if (lru && lru_pinned) {
- lru->lru_num_pinned--;
+ lru->num_pinned--;
// move from pintail -> bot
- if (lru_list == &lru->lru_pintail) {
- lru->lru_pintail.remove(this);
- lru->lru_bot.insert_tail(this);
+ if (lru_link.get_list() == &lru->pintail) {
+ lru->lru_bottouch(this);
}
- lru->lru_adjust();
}
lru_pinned = false;
}
class Item : public LRUObject {
public:
int id;
- explicit Item(int v) : id(v) {}
+ Item() : id(0) {}
+ Item(int i) : id(i) {}
+ void set(int i) {id = i;}
};
TEST(lru, InsertTop) {
- LRU lru = LRU(10);
-
- lru.lru_set_midpoint(.5); // 50% of 10 elements.
- for (int i=0; i<100; i++) {
- lru.lru_insert_top(new Item(i));
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.5); // 50% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_top(&items[i]);
}
- ASSERT_EQ(5U, lru.lru_get_top());
- ASSERT_EQ(95U, lru.lru_get_bot());
+ ASSERT_EQ(50U, lru.lru_get_top());
+ ASSERT_EQ(50U, lru.lru_get_bot());
ASSERT_EQ(100U, lru.lru_get_size());
ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
}
TEST(lru, InsertMid) {
- LRU lru = LRU(10);
-
- for (int i=0; i<100; i++) {
- lru.lru_insert_mid(new Item(i));
+ LRU lru;
+ static const int n = 102;
+ Item items[n];
+
+ lru.lru_set_midpoint(.7); // 70% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_mid(&items[i]);
}
- ASSERT_EQ(0U, lru.lru_get_top());
- ASSERT_EQ(100U, lru.lru_get_bot());
- ASSERT_EQ(100U, lru.lru_get_size());
+ ASSERT_EQ(71U, lru.lru_get_top());
+ ASSERT_EQ(31U, lru.lru_get_bot());
+ ASSERT_EQ(102U, lru.lru_get_size());
ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
}
TEST(lru, InsertBot) {
- LRU lru = LRU(10);
-
- for (int i=0; i<100; i++) {
- lru.lru_insert_bot(new Item(i));
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.7); // 70% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_bot(&items[i]);
}
- ASSERT_EQ(0U, lru.lru_get_top());
- ASSERT_EQ(100U, lru.lru_get_bot());
+ ASSERT_EQ(70U, lru.lru_get_top());
+ ASSERT_EQ(30U, lru.lru_get_bot());
ASSERT_EQ(100U, lru.lru_get_size());
ASSERT_EQ(99, (static_cast<Item*>(lru.lru_expire()))->id);
}
TEST(lru, Adjust) {
- LRU lru = LRU(10);
-
- lru.lru_set_midpoint(.6); // 60% of 10 elements.
- for (int i=0; i<100; i++) {
- lru.lru_touch(new Item(i));
+ LRU lru;
+ static const int n = 100;
+ Item items[n];
+
+ lru.lru_set_midpoint(.6); // 60% of elements.
+ for (int i=0; i<n; i++) {
+ items[i].set(i);
+ lru.lru_insert_top(&items[i]);
+ if (i % 5 == 0)
+ items[i].lru_pin();
}
- ASSERT_EQ(6U, lru.lru_get_top());
- ASSERT_EQ(94U, lru.lru_get_bot());
+ ASSERT_EQ(48U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(52U, lru.lru_get_bot());
ASSERT_EQ(100U, lru.lru_get_size());
- lru.lru_clear();
-
- lru.lru_set_midpoint(1.2); // 120% of 10 elements.
- for (int i=0; i<100; i++) {
- lru.lru_touch(new Item(i));
- }
- ASSERT_EQ(12U, lru.lru_get_top());
- ASSERT_EQ(88U, lru.lru_get_bot());
- ASSERT_EQ(100U, lru.lru_get_size());
+ ASSERT_EQ(1, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(1U, lru.lru_get_pintail());
+ ASSERT_EQ(47U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(51U, lru.lru_get_bot());
+ ASSERT_EQ(99U, lru.lru_get_size());
+ ASSERT_EQ(2, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(1U, lru.lru_get_pintail());
+ ASSERT_EQ(46U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(51U, lru.lru_get_bot());
+ ASSERT_EQ(98U, lru.lru_get_size());
+ ASSERT_EQ(3, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(4, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(6, (static_cast<Item*>(lru.lru_expire()))->id);
+ ASSERT_EQ(2U, lru.lru_get_pintail());
+ ASSERT_EQ(45U, lru.lru_get_top()); /* 60% of unpinned */
+ ASSERT_EQ(48U, lru.lru_get_bot());
+ ASSERT_EQ(95U, lru.lru_get_size());
}
TEST(lru, Pinning) {
- LRU lru = LRU();
+ LRU lru;
- Item *ob0 = new Item(0);
- Item *ob1 = new Item(1);
+ Item ob0(0), ob1(1);
// test before ob1 are in a LRU
- ob1->lru_pin();
- ASSERT_FALSE(ob1->lru_is_expireable());
+ ob1.lru_pin();
+ ASSERT_FALSE(ob1.lru_is_expireable());
- ob1->lru_unpin();
- ASSERT_TRUE(ob1->lru_is_expireable());
+ ob1.lru_unpin();
+ ASSERT_TRUE(ob1.lru_is_expireable());
// test when ob1 are in a LRU
- lru.lru_touch(ob0);
- lru.lru_touch(ob1);
+ lru.lru_insert_top(&ob0);
+ lru.lru_insert_top(&ob1);
- ob1->lru_pin();
- ob1->lru_pin(); // Verify that, one incr.
+ ob1.lru_pin();
+ ob1.lru_pin(); // Verify that, one incr.
ASSERT_EQ(1U, lru.lru_get_num_pinned());
- ASSERT_FALSE(ob1->lru_is_expireable());
+ ASSERT_FALSE(ob1.lru_is_expireable());
- ob1->lru_unpin();
- ob1->lru_unpin(); // Verify that, one decr.
+ ob1.lru_unpin();
+ ob1.lru_unpin(); // Verify that, one decr.
ASSERT_EQ(0U, lru.lru_get_num_pinned());
- ASSERT_TRUE(ob1->lru_is_expireable());
+ ASSERT_TRUE(ob1.lru_is_expireable());
ASSERT_EQ(0, (static_cast<Item*>(lru.lru_expire()))->id);
- ob0->lru_pin();
+ ob0.lru_pin();
ASSERT_EQ(1, (static_cast<Item*>(lru.lru_expire()))->id);
}