Remove util/lfu_table.h

This is unused.
Esse commit está contido em:
Jordan DeLong
2013-04-20 12:12:57 -07:00
commit de Sara Golemon
commit 7449c3afea
2 arquivos alterados com 0 adições e 779 exclusões
-142
Ver Arquivo
@@ -16,7 +16,6 @@
#include <test/test_util.h>
#include <util/logger.h>
#include <util/lfu_table.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/shared/shared_string.h>
#include <runtime/base/zend/zend_string.h>
@@ -37,7 +36,6 @@ TestUtil::TestUtil() {
bool TestUtil::RunTests(const std::string &which) {
bool ret = true;
//RUN_TEST(TestLFUTable);
RUN_TEST(TestSharedString);
RUN_TEST(TestCanonicalize);
RUN_TEST(TestHDF);
@@ -66,146 +64,6 @@ struct IEq {
bool operator()(int i, int j) const { return i == j; }
};
typedef LFUTable<int, int, IHash, IEq> TestMap;
bool TestUtil::TestLFUTable() {
{
TestMap table(1, 5, 2);
table.insert(1,1);
int r = 0;
VERIFY(table.lookup(1, r) && r == 1);
VERIFY(table.lookup(1, r) && r == 1);
VERIFY(table.lookup(1, r) && r == 1);
}
{
TestMap table(100, 5, 2);
for (int i = 0; i < 10; i++) {
table.insert(i,i);
}
VERIFY(table.check());
int r = 0;
VERIFY(!table.lookup(0, r));
VERIFY(!table.lookup(1, r));
VERIFY(!table.lookup(2, r));
VERIFY(!table.lookup(3, r));
VERIFY(!table.lookup(4, r));
VERIFY(table.lookup(5, r) && r == 5);
VERIFY(table.lookup(6, r) && r == 6);
VERIFY(table.lookup(7, r) && r == 7);
VERIFY(table.lookup(8, r) && r == 8);
VERIFY(table.lookup(9, r) && r == 9);
VERIFY(table.check());
}
{
TestMap table(1, 5, 2);
for (int i = 0; i < 5; i++) {
table.insert(i,i);
}
sleep(1);
for (int i = 5; i < 10; i++) {
table.insert(i,i);
}
VERIFY(table.check());
int r = 0;
VERIFY(!table.lookup(0, r));
VERIFY(!table.lookup(1, r));
VERIFY(!table.lookup(2, r));
VERIFY(!table.lookup(3, r));
VERIFY(!table.lookup(4, r));
VERIFY(table.lookup(5, r) && r == 5);
VERIFY(table.lookup(6, r) && r == 6);
VERIFY(table.lookup(7, r) && r == 7);
VERIFY(table.lookup(8, r) && r == 8);
VERIFY(table.lookup(9, r) && r == 9);
VERIFY(table.check());
}
{
TestMap table(1, 5, 2);
for (int i = 0; i < 5; i++) {
table.insert(i,i);
}
int r = 0;
VERIFY(table.lookup(0, r) && r == 0);
VERIFY(table.lookup(0, r) && r == 0);
VERIFY(table.lookup(3, r) && r == 3);
sleep(1);
for (int i = 5; i < 8; i++) {
table.insert(i,i);
}
VERIFY(table.lookup(0, r) && r == 0);
VERIFY(!table.lookup(1, r));
VERIFY(!table.lookup(2, r));
VERIFY(table.lookup(3, r) && r == 3);
VERIFY(!table.lookup(4, r));
VERIFY(table.lookup(5, r) && r == 5);
VERIFY(table.lookup(6, r) && r == 6);
VERIFY(table.lookup(7, r) && r == 7);
int ct = 0;
for (TestMap::const_iterator it = table.begin();
it != table.end(); ++it) {
ct++;
VERIFY(it.first() == it.second());
}
VERIFY(ct == 5);
}
{
TestMap table(1, 5, 2);
for (int i = 0; i < 5; i++) {
table[i] = i;
}
int r = 0;
VERIFY(table[0] == 0);
VERIFY(table[0] == 0);
VERIFY(table[3] == 3);
sleep(1);
for (int i = 5; i < 8; i++) {
table[i] = i;
}
VERIFY(table.check());
VERIFY(table.lookup(0, r) && r == 0);
VERIFY(!table.lookup(1, r));
VERIFY(!table.lookup(2, r));
VERIFY(table.lookup(3, r) && r == 3);
VERIFY(!table.lookup(4, r));
VERIFY(table.lookup(5, r) && r == 5);
VERIFY(table.lookup(6, r) && r == 6);
VERIFY(table.lookup(7, r) && r == 7);
VERIFY(table.check());
int ct = 0;
for (TestMap::const_iterator it = table.begin();
it != table.end(); ++it) {
ct++;
VERIFY(it.first() == it.second());
}
VERIFY(ct == 5);
}
{
class Reader : public TestMap::AtomicReader {
public:
void read(int const &k, int const &v) {
}
};
class Updater : public TestMap::AtomicUpdater {
public:
bool update(int const &k, int &v, bool newly) {
v = k;
return false;
}
};
Reader r;
Updater u;
TestMap table(1, 5, 2);
for (int i = 0; i < 5; i++) {
table.atomicUpdate(i, u, true);
}
for (int i = 0; i < 5; i++) {
VERIFY(table.atomicRead(i, r));
}
}
return Count(true);
}
bool TestUtil::TestSharedString() {
{
SharedString foo = "foo";
-637
Ver Arquivo
@@ -1,637 +0,0 @@
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_UTIL_LFU_TABLE_H_
#define incl_HPHP_UTIL_LFU_TABLE_H_
#include <util/base.h>
#include <util/lock.h>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
template<class K, class V>
class LFUNullDestructor {
public:
void operator()(const K &k, V &v) {}
};
/**
* A lookup table with a maximum size. Once it reaches maximum size,
* inserts will evict the least frequently used item.
*
* It consists of 3 parts: a queue, a heap, and a map.
* The map is used for lookup by key. The queue is a staging area for
* values that are too new to have an accurate frequency, and the heap
* serves as a priority queue for looking up the element that has
* the lowest frequency.
*
* Frequencies are updated only when the hit count hits a multiple of the
* period option. When a frequency is updated, the heap is also updated
* to maintain the heap property. Frequency should be locked by the queue
* lock since it must not change during heap operations.
*
* There are two locks that make it thread safe. There is read/write lock
* for the map, and a lock for the queue/heap. The acquisition order
* is map then queue/heap.
*
* An element is in the map if it is in the queue xor the heap.
* If max capacity is 0, then all queue operations are disabled.
* Elements can be added to the map but not the queue.
*/
template<class K, class V, class H, class E,
class D = LFUNullDestructor<K, V> >
class LFUTable {
class Node {
public:
Node(const K &k) : key(k), prev(nullptr), next(nullptr), hits(0), heapIndex(0),
immortal(false), m_freq(0), m_timestamp(time(nullptr)) {}
Node(const K &k, const V &v)
: key(k), val(v), prev(nullptr), next(nullptr), hits(0), heapIndex(0),
immortal(false), m_freq(0), m_timestamp(time(nullptr)) {}
~Node() {
D d;
d(key, val);
}
const K &key;
V val;
Node *prev;
Node *next;
std::atomic<uint64_t> hits;
uint heapIndex;
bool immortal;
// Frequency updates must be accompanied by heap updates since otherwise
// it may break the heap property. It should probably be under lock too.
bool updateFrequency() {
double lifetime = time(nullptr) - m_timestamp;
double oldFreq = m_freq;
if (lifetime > 0) {
m_freq = hits / lifetime;
}
return m_freq > oldFreq;
}
double frequency() const {
return m_freq;
}
time_t timestamp() {
return m_timestamp;
}
private:
double m_freq;
time_t m_timestamp;
};
// The map
class Map : public hphp_hash_map<K, Node*, H, E> {};
//typedef std::map<K, Node*> Map;
public:
class LFUTableConstIterator;
class LFUTableIterator {
public:
LFUTableIterator(typename LFUTable::Map::iterator &it) : m_it(it) {}
LFUTableIterator(const LFUTableIterator &it) : m_it(it.m_it) {}
const K &first() const {
return m_it->first;
}
V &second() const {
return m_it->second->val;
}
LFUTableIterator &operator++() {
++m_it;
return *this;
}
bool operator==(const LFUTableIterator &it) const {
return m_it == it.m_it;
}
bool operator!=(const LFUTableIterator &it) const {
return m_it != it.m_it;
}
private:
friend class LFUTable::LFUTableConstIterator;
friend class LFUTable;
typename LFUTable::Map::iterator m_it;
};
class LFUTableConstIterator {
public:
LFUTableConstIterator(typename LFUTable::Map::const_iterator &it)
: m_it(it) {}
LFUTableConstIterator(const LFUTableConstIterator &it) : m_it(it.m_it) {
}
LFUTableConstIterator(const LFUTableIterator &it) : m_it(it.m_it) {
}
const K &first() const {
return m_it->first;
}
const V &second() const {
return m_it->second->val;
}
LFUTableConstIterator &operator++() {
++m_it;
return *this;
}
bool operator==(const LFUTableConstIterator &it) const {
return m_it == it.m_it;
}
bool operator!=(const LFUTableConstIterator &it) const {
return m_it != it.m_it;
}
private:
friend class LFUTable;
typename LFUTable::Map::const_iterator m_it;
};
/**
* AtomicReader is used to perform a lookup under the internal
* lock of the table. It takes the read lock.
*/
class AtomicReader {
public:
virtual ~AtomicReader() {}
virtual void read(const K &key, const V &val) = 0;
};
/**
* AtomicUpdater is used to perform a update/insert under the internal
* lock of the table. It takes the write lock.
* The update method is given a reference to the value in the table and
* a bool that specifies if the element was newly added.
* It the method returns false, the element is deleted.
*/
class AtomicUpdater {
public:
virtual ~AtomicUpdater() {}
virtual bool update(const K &key, V &val, bool newlyCreated) = 0;
};
public:
LFUTable(time_t maturity, size_t maxCap, int updatePeriod)
: m_head(nullptr), m_tail(nullptr), m_immortalCount(0),
m_maturityThreshold(maturity), m_maximumCapacity(maxCap),
m_updatePeriod(updatePeriod) {
//Lfu table is currently buggy and at the moment not worth fixing
assert(false);
}
~LFUTable() {
clear();
}
typedef LFUTableConstIterator const_iterator;
typedef LFUTableIterator iterator;
const_iterator begin() const {
return const_iterator(m_map.begin());
}
const_iterator end() const {
return const_iterator(m_map.end());
}
iterator begin() {
typename Map::iterator it = m_map.begin();
return iterator(it);
}
iterator end() {
typename Map::iterator it = m_map.end();
return iterator(it);
}
void insert(const K &k, const V &v) {
WriteLock lock(m_mapLock);
if (_contains(k)) {
_erase(k);
}
_makeRoom();
// Add to the map
typename Map::iterator ins = m_map.insert(std::pair<const K, Node*>(k,nullptr))
.first;
Node *n = new Node(ins->first, v);
ins->second = n;
// Add to queue
insertQueue(n);
}
bool atomicRead(const K &k, AtomicReader &reader) {
ReadLock lock(m_mapLock);
Node *n = _getNode(k, false);
if (n) {
reader.read(n->key, n->val);
return true;
}
return false;
}
void atomicForeach(AtomicReader &reader) {
ReadLock lock(m_mapLock);
for (typename Map::const_iterator it = m_map.begin();
it != m_map.end(); ++it) {
reader.read(it->first, it->second->val);
}
}
void atomicUpdate(const K &k, AtomicUpdater &updater, bool createNew,
bool immortal = false) {
WriteLock lock(m_mapLock);
bool erase = false;
Node *n = _getNode(k, false);
bool created = false;
if (!n && createNew) {
n = _createNode(k, immortal);
created = true;
}
if (n) {
erase = updater.update(n->key, n->val, created);
}
if (erase) {
_erase(k);
}
}
void erase(const K &k) {
WriteLock lock(m_mapLock);
_erase(k);
}
void erase(const iterator &it) {
WriteLock lock(m_mapLock);
_erase(it);
}
bool lookup(const K &k, V &result) {
ReadLock lock(m_mapLock);
Node *n = _getNode(k, false);
if (n) {
result = n->val;
return true;
}
return false;
}
iterator find(const K &k) {
ReadLock lock(m_mapLock);
typename Map::iterator it = m_map.find(k);
if (it != m_map.end()) {
Node *n = it->second;
_bumpNode(n);
}
return iterator(it);
}
V &operator[](const K &k) {
WriteLock lock(m_mapLock);
return _getNode(k, true)->val;
}
size_t size() const {
return m_map.size();
}
size_t maximumCapacity() const {
return m_maximumCapacity;
}
size_t immortalCount() const {
return m_immortalCount;
}
void clear() {
WriteLock lock(m_mapLock);
typename Map::iterator it = m_map.begin();
while (it != m_map.end()) {
typename Map::iterator cit = it++;
Node *n = cit->second;
if (n->immortal) {
// Trade immortal for mortal
++m_maximumCapacity;
--m_immortalCount;
} else {
removeQueue(cit->second);
}
m_map.erase(cit);
delete n;
}
assert(m_head == nullptr);
assert(m_tail == nullptr);
assert(m_heap.size() == 0);
}
bool check() {
WriteLock lock(m_mapLock);
Lock qlock(m_queueLock);
bool fail = false;
Node *prev = nullptr;
Node *n = m_head;
while (n) {
if (m_map.find(n->key) == m_map.end()) {
fail = true;
Logger::Error("Value in queue not in map");
assert(!fail);
}
if (n->prev != prev) {
fail = true;
Logger::Error("Queue list corrupted");
assert(!fail);
}
prev = n;
n = n->next;
if (!n && prev != m_tail) {
fail = true;
Logger::Error("Queue tail incorrect");
assert(!fail);
}
}
uint hsize = m_heap.size();
for (uint i = 0; i < hsize; i++) {
Node *n = m_heap[i];
if (m_map.find(n->key) == m_map.end()) {
fail = true;
Logger::Error("Value in queue not in heap");
assert(!fail);
}
size_t child = (i+1) * 2;
if (child <= hsize && n->frequency() > m_heap[child-1]->frequency()) {
fail = true;
Logger::Error("Heap property violated");
assert(!fail);
}
child++;
if (child <= hsize && n->frequency() > m_heap[child-1]->frequency()) {
fail = true;
Logger::Error("Heap property violated");
assert(!fail);
}
}
return !fail;
}
private:
private:
//////////////////////////////////////////////////////////////////////////////
// These methods are to be used when the map lock is already acquired.
bool _contains(const K &k) {
return m_map.find(k) != m_map.end();
}
void _erase(const K &k) {
typename Map::iterator it = m_map.find(k);
if (it != m_map.end()) {
_erase(it);
}
}
void _erase(const iterator &it) {
Node *n = it.m_it->second;
m_map.erase(it.m_it);
if (n->immortal) {
// We trade an immortal for a mortal value
--m_immortalCount;
++m_maximumCapacity;
} else {
removeQueue(n);
}
delete n;
}
void _bumpNode(Node *n) {
if (!m_maximumCapacity) return;
if (n->hits.fetch_add(1, std::memory_order_relaxed) % m_updatePeriod == 0) {
Lock lock(m_queueLock);
// hits could have increased between incrementing and taking the lock,
// but it does not matter.
// It is also possible (if unlikely) that two threads could be trying to
// do this update, but that doesn't matter either.
bool increase = n->updateFrequency();
_updateQueue(n, increase);
}
}
void _makeRoom() {
if (!m_maximumCapacity) return;
while (size() - m_immortalCount >= m_maximumCapacity) {
Node *m = popQueue();
assert(m);
m_map.erase(m->key);
delete m;
}
}
Node *_getNode(const K &k, bool force, bool immortal = false) {
typename Map::iterator it = m_map.find(k);
if (it != m_map.end()) {
_bumpNode(it->second);
return it->second;
}
if (force) {
return _createNode(k, immortal);
} else {
return nullptr;
}
}
Node *_createNode(const K &k, bool immortal = false) {
if (immortal) {
++m_immortalCount;
} else {
_makeRoom();
}
// Add to the map
typename Map::iterator ins = m_map.insert(std::pair<const K, Node*>(k,nullptr))
.first;
Node *n = new Node(ins->first);
ins->second = n;
n->immortal = immortal;
if (!immortal) {
// Add to queue
insertQueue(n);
}
return n;
}
void _dumpStatus() {
std::cout << "in map:\n";
for (typename Map::const_iterator it = m_map.begin(); it != m_map.end();
++it) {
std::cout << it->first << " : " << it->second->frequency() << "\n";
}
if (!m_maximumCapacity) return;
std::cout << "in queue:\n";
Node *n = m_head;
while (n) {
std::cout << n->key << " : " << n->frequency() << "\n";
n = n->next;
}
std::cout << "in heap\n";
for (uint i = 0; i < m_heap.size(); i++) {
std::cout << m_heap[i]->key << " : " << m_heap[i]->frequency() << "\n";
}
}
private:
//////////////////////////////////////////////////////////////////////////////
// Queue interface methods
// Add item to queue
void insertQueue(Node *item) {
if (!m_maximumCapacity) return;
Lock lock(m_queueLock);
// Add to head of list
item->heapIndex = 0;
item->prev = nullptr;
item->next = m_head;
if (m_head) m_head->prev = item;
m_head = item;
if (!m_tail) m_tail = item;
_shiftMature();
}
void removeQueue(Node *item) {
if (!m_maximumCapacity) return;
Lock lock(m_queueLock);
if (item == m_head) {
m_head = item->next;
}
if (item == m_tail) {
m_tail = item->prev;
}
if (item->prev) {
item->prev->next = item->next;
}
if (item->next) {
item->next->prev = item->prev;
}
item->prev = item->next = nullptr;
if (item->heapIndex != 0) {
int pos = item->heapIndex;
int last = m_heap.size();
item->heapIndex = 0;
m_heap[pos-1] = m_heap[last-1];
m_heap[pos-1]->heapIndex = pos;
m_heap.pop_back();
_heapifyDown(pos);
}
}
// Pop the minimum
Node *popQueue() {
if (!m_maximumCapacity) return nullptr;
Lock lock(m_queueLock);
_shiftMature();
if (m_heap.size() == 0) {
Node *res = m_tail;
if (!res) return nullptr;
if (res->prev) {
res->prev->next = nullptr;
}
m_tail = res->prev;
res->prev = nullptr;
if (res == m_head) m_head = nullptr;
return res;
}
Node *res = m_heap[0];
res->heapIndex = 0;
m_heap[0] = m_heap[m_heap.size() - 1];
m_heap[0]->heapIndex = 1;
m_heap.pop_back();
_heapifyDown(1);
return res;
}
//////////////////////////////////////////////////////////////////////////////
// Queue helper methods
void _shiftMature() {
// Move mature items to heap
time_t now = time(nullptr);
while (m_tail && ((now - m_tail->timestamp()) >= m_maturityThreshold)) {
Node *last = m_tail;
if (last->prev) {
last->prev->next = nullptr;
}
m_tail = last->prev;
if (m_head == last) m_head = nullptr;
last->prev = nullptr;
last->updateFrequency();
_heapInsert(last);
}
}
void _updateQueue(const Node *item, bool increase) {
if (!m_maximumCapacity) return;
if (item->heapIndex != 0) {
if (increase) {
_heapifyDown(item->heapIndex);
} else {
_heapifyUp(item->heapIndex);
}
}
}
void _heapInsert(Node *item) {
m_heap.push_back(item);
item->heapIndex = m_heap.size();
int pos = m_heap.size();
_heapifyUp(pos);
}
void _heapifyUp(int pos) {
while (pos != 1) {
Node *&child = m_heap[pos - 1];
Node *&parent = m_heap[pos/2 - 1];
if (parent->frequency() > child->frequency()) {
std::swap(child, parent);
child->heapIndex = pos;
parent->heapIndex = pos/2;
pos = pos/2;
} else {
break;
}
}
}
void _heapifyDown(int pos) {
int size = m_heap.size();
for (;;) {
int child;
if (pos * 2 > size) break;
if (pos * 2 + 1 > size || (m_heap[pos*2 - 1]->frequency() <
m_heap[pos*2]->frequency())) {
child = pos * 2;
} else {
child = pos * 2 + 1;
}
if (m_heap[pos - 1]->frequency() > m_heap[child - 1]->frequency()) {
std::swap(m_heap[pos - 1], m_heap[child - 1]);
m_heap[pos - 1]->heapIndex = pos;
m_heap[child - 1]->heapIndex = child;
pos = child;
} else {
break;
}
}
}
private:
// The queue. A doubly linked list since I need to remove elements at will.
Node *m_head;
Node *m_tail;
// The heap
std::vector<Node*> m_heap;
Map m_map;
size_t m_immortalCount;
// Locks in acquisition order
ReadWriteMutex m_mapLock;
Mutex m_queueLock;
// Options
time_t m_maturityThreshold;
size_t m_maximumCapacity;
int m_updatePeriod;
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // incl_HPHP_UTIL_LFU_TABLE_H_