3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00
z3/lib/lru_cache.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

425 lines
13 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
lru_cache.cpp
Abstract:
expr -> expr LRU cache
Author:
Leonardo (leonardo) 2011-04-12
Notes:
--*/
#include"lru_cache.h"
#include"ast_ll_pp.h"
#define MIN_MAX_SIZE 1024
#define INITIAL_CAPACITY 128
lru_cache::cell * lru_cache::allocate(unsigned capacity) {
cell * mem = static_cast<cell*>(memory::allocate(sizeof(cell) * capacity));
memset(mem, 0, sizeof(cell)*capacity);
return mem;
}
void lru_cache::deallocate(cell * table) {
memory::deallocate(table);
}
lru_cache::cell * lru_cache::copy_table(cell * old_head, cell * new_table, unsigned new_capacity) {
SASSERT(old_head);
cell * it = old_head;
cell * new_head = 0;
cell * prev = 0;
do {
expr * k = it->m_key;
unsigned h = k->hash();
unsigned mask = new_capacity - 1;
unsigned idx = h & mask;
cell * begin = new_table + idx;
cell * end = new_table + new_capacity;
cell * curr = begin;
for (; curr != end; ++curr) {
if (curr->m_key == 0) {
curr->m_key = k;
curr->m_value = it->m_value;
LCS_CODE(curr->m_hits = it->m_hits;);
LCS_CODE(curr->m_birthday = it->m_birthday;);
if (prev == 0) {
new_head = curr;
}
else {
prev->m_next = curr;
curr->m_prev = prev;
}
goto end;
}
}
for (curr = new_table; curr != begin; ++curr) {
if (curr->m_key == 0) {
curr->m_key = k;
curr->m_value = it->m_value;
SASSERT(prev != 0);
prev->m_next = curr;
curr->m_prev = prev;
}
goto end;
}
UNREACHABLE();
end:
prev = curr;
it = it->m_next;
}
while (it != old_head);
prev->m_next = new_head;
new_head->m_prev = prev;
return new_head;
}
void lru_cache::expand_table() {
SASSERT(m_head);
unsigned new_capacity = m_capacity * 2;
TRACE("lru_cache", tout << "expanding table new_capacity: " << new_capacity << "\n";);
cell * new_table = allocate(new_capacity);
m_head = copy_table(m_head, new_table, new_capacity);
deallocate(m_table);
m_table = new_table;
m_capacity = new_capacity;
m_num_deleted = 0;
SASSERT(check_invariant());
}
void lru_cache::remove_deleted() {
SASSERT(m_head);
TRACE("lru_cache", tout << "removing deleted entries\n";);
cell * new_table = allocate(m_capacity);
m_head = copy_table(m_head, new_table, m_capacity);
deallocate(m_table);
m_table = new_table;
m_num_deleted = 0;
SASSERT(check_invariant());
}
void lru_cache::init() {
if (m_max_size < MIN_MAX_SIZE)
m_max_size = MIN_MAX_SIZE;
m_size = 0;
m_capacity = INITIAL_CAPACITY;
m_table = allocate(m_capacity);
m_head = 0;
m_num_deleted = 0;
}
lru_cache::lru_cache(ast_manager & m):
m_manager(m),
m_max_size(m.get_num_asts() * 100) {
init();
TRACE("lru_cache", tout << "new lru cache, max-size: " << m_max_size << "\n";);
}
lru_cache::lru_cache(ast_manager & m, unsigned max_size):
m_manager(m),
m_max_size(max_size) {
init();
}
void lru_cache::dec_refs() {
if (m_head) {
cell * curr = m_head;
#ifdef Z3DEBUG
unsigned sz = 0;
#endif
do {
m_manager.dec_ref(curr->m_key);
m_manager.dec_ref(curr->m_value);
curr = curr->m_next;
DEBUG_CODE(sz++;);
}
while (curr != m_head);
SASSERT(sz == m_size);
}
}
lru_cache::~lru_cache() {
TRACE("lru_cache", tout << "destructor invoked size: " << m_size << ", m_head: " << m_head << "\n";);
LCS_CODE({
if (m_head) {
unsigned used = 0;
cell * curr = m_head;
do {
if (curr->m_hits > 0)
used++;
curr = curr->m_next;
}
while (curr != m_head);
verbose_stream() << "[lru_cache] num-used: " << used << " size: " << m_size << "\n";
}
});
SASSERT(check_invariant());
dec_refs();
deallocate(m_table);
}
void lru_cache::del_least_used() {
SASSERT(m_head);
SASSERT(m_size >= 2);
cell * c = m_head->m_prev;
TRACE("lru_cache", tout << "del least used: " << mk_bounded_pp(c->m_key, m_manager, 3) << "\n";);
LCS_CODE({
static unsigned non_zero = 0;
static unsigned long long total_hits;
static unsigned counter = 0;
if (c->m_hits > 0) {
counter++;
total_hits += c->m_hits;
non_zero++;
if (non_zero % 1000 == 0)
verbose_stream() << "[lru_cache] cell with non-zero hits was deleted: " << non_zero << " avg: " << ((double)total_hits/(double) counter) << std::endl;
}
});
SASSERT(c->m_prev != c);
SASSERT(c->m_next != c);
m_manager.dec_ref(c->m_key);
m_manager.dec_ref(c->m_value);
c->m_prev->m_next = c->m_next;
c->m_next->m_prev = c->m_prev;
SASSERT(m_head->m_prev == c->m_prev);
c->m_key = reinterpret_cast<expr*>(1);
c->m_prev = 0;
c->m_next = 0;
m_size--;
m_num_deleted++;
CASSERT("lru_cache", check_invariant());
if (m_num_deleted * 3 > m_capacity)
remove_deleted();
}
void lru_cache::add_front(cell * c) {
SASSERT(c->m_next == 0);
SASSERT(c->m_prev == 0);
if (m_head == 0) {
c->m_next = c;
c->m_prev = c;
m_head = c;
}
else {
c->m_prev = m_head->m_prev;
c->m_next = m_head;
m_head->m_prev->m_next = c;
m_head->m_prev = c;
m_head = c;
}
CASSERT("lru_cache", check_invariant());
SASSERT(m_head == c);
}
void lru_cache::move_front(cell * c) {
SASSERT(m_head);
SASSERT(c->m_next);
SASSERT(c->m_prev);
if (m_head != c) {
c->m_prev->m_next = c->m_next;
c->m_next->m_prev = c->m_prev;
c->m_prev = m_head->m_prev;
c->m_next = m_head;
m_head->m_prev->m_next = c;
m_head->m_prev = c;
m_head = c;
}
CASSERT("lru_cache", check_invariant());
SASSERT(m_head == c);
}
void lru_cache::insert(expr * k, expr * v) {
LCS_CODE(m_time++;);
if (m_size == m_max_size)
del_least_used();
else if (m_size * 2 > m_capacity)
expand_table();
SASSERT(m_size < m_max_size);
unsigned h = k->hash();
unsigned mask = m_capacity - 1;
unsigned idx = h & mask;
cell * begin = m_table + idx;
cell * end = m_table + m_capacity;
cell * curr = begin;
cell * del_cell = 0;
#define INSERT_LOOP() \
if (curr->m_key == 0) { \
cell * new_cell; \
if (del_cell) { \
new_cell = del_cell; \
m_num_deleted--; \
} \
else { \
new_cell = curr; \
} \
m_manager.inc_ref(k); \
m_manager.inc_ref(v); \
new_cell->m_key = k; \
new_cell->m_value = v; \
LCS_CODE(new_cell->m_hits = 0;); \
LCS_CODE(new_cell->m_birthday = m_time;); \
m_size++; \
add_front(new_cell); \
return; \
} \
if (curr->m_key == reinterpret_cast<expr*>(1)) { \
del_cell = curr; \
continue; \
} \
if (curr->m_key == k) { \
m_manager.inc_ref(v); \
m_manager.dec_ref(curr->m_value); \
curr->m_value = v; \
LCS_CODE(curr->m_hits = 0;); \
LCS_CODE(curr->m_birthday = m_time;); \
move_front(curr); \
return; \
}
for (; curr != end; ++curr) {
INSERT_LOOP();
}
for (curr = m_table; curr != begin; ++curr) {
INSERT_LOOP();
}
UNREACHABLE();
}
expr * lru_cache::find(expr * k) {
unsigned h = k->hash();
unsigned mask = m_capacity - 1;
unsigned idx = h & mask;
#ifdef LRU_CACHE_STATISTICS
static unsigned long long total_age = 0;
static unsigned long long max_time = 0;
static unsigned counter = 0;
#define COLLECT() \
if (curr->m_hits == 0) { \
counter ++; \
unsigned age = m_time - curr->m_birthday; \
if (age > max_time) \
max_time = age; \
total_age += age; \
if (counter % 1000 == 0) \
verbose_stream() << "[lru_cache] avg time for first hit: " << ((double) total_age / (double) counter) << " max time: " << max_time << "\n"; \
}
#endif
cell * begin = m_table + idx;
cell * end = m_table + m_capacity;
cell * curr = begin;
for (; curr != end; ++curr) {
if (curr->m_key == k) {
// LCS_CODE(COLLECT());
LCS_CODE(if (curr->m_hits == 0 && m_time - curr->m_birthday >= 5000) return 0;)
LCS_CODE(curr->m_hits++;);
move_front(curr);
return curr->m_value;
}
if (curr->m_key == 0)
return 0;
}
for (curr = m_table; curr != begin; ++curr) {
if (curr->m_key == k) {
// LCS_CODE(COLLECT());
LCS_CODE(curr->m_hits++;);
LCS_CODE(if (curr->m_hits == 0 && m_time - curr->m_birthday >= 5000) return 0;);
move_front(curr);
return curr->m_value;
}
if (curr->m_key == 0)
return 0;
}
return 0;
}
void lru_cache::reset() {
TRACE("lru_cache", tout << "reset... m_size: " << m_size << "\n";);
LCS_CODE(m_time = 0;);
if (m_head) {
cell * curr = m_head;
#ifdef Z3DEBUG
unsigned sz = 0;
#endif
do {
m_manager.dec_ref(curr->m_key);
m_manager.dec_ref(curr->m_value);
cell * next = curr->m_next;
curr->m_key = 0;
curr->m_value = 0;
curr->m_next = 0;
curr->m_prev = 0;
LCS_CODE(curr->m_hits = 0;);
LCS_CODE(curr->m_birthday = 0;);
curr = next;
DEBUG_CODE(sz++;);
}
while (curr != m_head);
SASSERT(sz == m_size);
m_head = 0;
m_size = 0;
SASSERT(check_invariant());
}
}
void lru_cache::cleanup() {
dec_refs();
deallocate(m_table);
m_capacity = INITIAL_CAPACITY;
m_table = allocate(m_capacity);
m_head = 0;
m_size = 0;
m_num_deleted = 0;
}
bool lru_cache::check_invariant() const {
SASSERT(m_size <= m_max_size);
cell * begin = m_table;
cell * end = m_table + m_capacity;
unsigned sz = 0;
if (m_head) {
cell * curr = m_head;
do {
sz++;
SASSERT(curr->m_key != 0 && curr->m_key != reinterpret_cast<expr*>(1));
SASSERT(curr->m_next->m_prev == curr);
SASSERT(curr->m_prev->m_next == curr);
SASSERT(curr < end);
SASSERT(curr >= begin);
curr = curr->m_next;
}
while (curr != m_head);
}
SASSERT(m_size == sz);
sz = 0;
unsigned num_deleted = 0;
for (cell * it = begin; it != end; it++) {
if (it->m_key == reinterpret_cast<expr*>(1)) {
num_deleted++;
continue;
}
if (it->m_key != 0) {
sz++;
}
}
SASSERT(m_size == sz);
SASSERT(m_num_deleted == num_deleted);
return true;
}