summaryrefslogtreecommitdiffstats
path: root/net/disk_cache/backend_impl.cc
diff options
context:
space:
mode:
authorrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-06 20:05:34 +0000
committerrvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-06 20:05:34 +0000
commit90c7aa0fe476246b74608e564ea09f0d2a4951da (patch)
treed4d0c7114c09a537f3493efe866a1e6a7f74a944 /net/disk_cache/backend_impl.cc
parent6d1ef0ff8c7debfd17d00a2c2649f853231d50d8 (diff)
downloadchromium_src-90c7aa0fe476246b74608e564ea09f0d2a4951da.zip
chromium_src-90c7aa0fe476246b74608e564ea09f0d2a4951da.tar.gz
chromium_src-90c7aa0fe476246b74608e564ea09f0d2a4951da.tar.bz2
New disk cache eviction algorithm (partial implementation).
Disabled by a #def. When enabled, file format 2.1 is used, with a possible update from ver 2.0. If a version with this code disabled is run after the upgrade, it will just discard the file and go back to 2.0. We now put some of those extra list to use! Entries are separated into various lists depending on how often are used, and we keep track of previously evicted entries. If the new algorithm is not enabled, most of the code just goes through a different path (with the old code instead of the new one). One notable exception is OpenFollowingEntry, used to enumerate the entries on the cache; the code changed significantly to support the new version of the "cache iterator", but functionally should do the same as the current code. Review URL: http://codereview.chromium.org/27345 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11145 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/disk_cache/backend_impl.cc')
-rw-r--r--net/disk_cache/backend_impl.cc281
1 files changed, 233 insertions, 48 deletions
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc
index 52d8f65..9f560cc 100644
--- a/net/disk_cache/backend_impl.cc
+++ b/net/disk_cache/backend_impl.cc
@@ -17,6 +17,9 @@
#include "net/disk_cache/hash.h"
#include "net/disk_cache/file.h"
+// Uncomment this to use the new eviction algorithm.
+// #define USE_NEW_EVICTION
+
using base::Time;
using base::TimeDelta;
@@ -184,6 +187,10 @@ bool BackendImpl::Init() {
if (init_)
return false;
+#ifdef USE_NEW_EVICTION
+ new_eviction_ = true;
+#endif
+
bool create_files = false;
if (!InitBackingStore(&create_files)) {
ReportError(ERR_STORAGE_ERROR);
@@ -231,7 +238,7 @@ bool BackendImpl::Init() {
if (!stats_.Init(this, &data_->header.stats))
return false;
- disabled_ = !rankings_.Init(this);
+ disabled_ = !rankings_.Init(this, new_eviction_);
eviction_.Init(this);
return !disabled_;
@@ -256,7 +263,16 @@ BackendImpl::~BackendImpl() {
int32 BackendImpl::GetEntryCount() const {
if (!index_)
return 0;
- return data_->header.num_entries;
+ // num_entries includes entries already evicted.
+ int32 not_deleted = data_->header.num_entries -
+ data_->header.lru.sizes[Rankings::DELETED];
+
+ if (not_deleted < 0) {
+ NOTREACHED();
+ not_deleted = 0;
+ }
+
+ return not_deleted;
}
bool BackendImpl::OpenEntry(const std::string& key, Entry** entry) {
@@ -272,6 +288,13 @@ bool BackendImpl::OpenEntry(const std::string& key, Entry** entry) {
return false;
}
+ if (ENTRY_NORMAL != cache_entry->entry()->Data()->state) {
+ // The entry was already evicted.
+ cache_entry->Release();
+ stats_.OnEvent(Stats::OPEN_MISS);
+ return false;
+ }
+
eviction_.OnOpenEntry(cache_entry);
DCHECK(entry);
*entry = cache_entry;
@@ -285,16 +308,24 @@ bool BackendImpl::CreateEntry(const std::string& key, Entry** entry) {
if (disabled_ || key.empty())
return false;
+ DCHECK(entry);
+ *entry = NULL;
+
Time start = Time::Now();
uint32 hash = Hash(key);
scoped_refptr<EntryImpl> parent;
Addr entry_address(data_->table[hash & mask_]);
if (entry_address.is_initialized()) {
+ // We have an entry already. It could be the one we are looking for, or just
+ // a hash conflict.
+ EntryImpl* old_entry = MatchEntry(key, hash, false);
+ if (old_entry)
+ return ResurrectEntry(old_entry, entry);
+
EntryImpl* parent_entry = MatchEntry(key, hash, true);
if (!parent_entry) {
- stats_.OnEvent(Stats::CREATE_MISS);
- Trace("create entry miss ");
+ NOTREACHED();
return false;
}
parent.swap(&parent_entry);
@@ -339,14 +370,11 @@ bool BackendImpl::CreateEntry(const std::string& key, Entry** entry) {
block_files_.GetFile(entry_address)->Store(cache_entry->entry());
block_files_.GetFile(node_address)->Store(cache_entry->rankings());
- data_->header.num_entries++;
- DCHECK(data_->header.num_entries > 0);
+ IncreaseNumEntries();
eviction_.OnCreateEntry(cache_entry);
if (!parent.get())
data_->table[hash & mask_] = entry_address.value();
- DCHECK(entry);
- *entry = NULL;
cache_entry.swap(reinterpret_cast<EntryImpl**>(entry));
UMA_HISTOGRAM_TIMES("DiskCache.CreateTime", Time::Now() - start);
@@ -451,8 +479,8 @@ bool BackendImpl::OpenNextEntry(void** iter, Entry** next_entry) {
}
void BackendImpl::EndEnumeration(void** iter) {
- Rankings::ScopedRankingsBlock rankings(&rankings_,
- reinterpret_cast<CacheRankingsBlock*>(*iter));
+ scoped_ptr<Rankings::Iterator> iterator(
+ reinterpret_cast<Rankings::Iterator*>(*iter));
*iter = NULL;
}
@@ -600,11 +628,26 @@ void BackendImpl::InternalDoomEntry(EntryImpl* entry) {
data_->table[hash & mask_] = child;
}
- data_->header.num_entries--;
- DCHECK(data_->header.num_entries >= 0);
+ if (!new_eviction_) {
+ DecreaseNumEntries();
+ }
+
stats_.OnEvent(Stats::DOOM_ENTRY);
}
+// An entry may be linked on the DELETED list for a while after being doomed.
+// This function is called when we want to remove it.
+void BackendImpl::RemoveEntry(EntryImpl* entry) {
+ if (!new_eviction_)
+ return;
+
+ DCHECK(ENTRY_DOOMED == entry->entry()->Data()->state);
+
+ Trace("Remove entry 0x%p", entry);
+ eviction_.OnDestroyEntry(entry);
+ DecreaseNumEntries();
+}
+
void BackendImpl::CacheEntryDestroyed() {
DecreaseNumRefs();
}
@@ -742,6 +785,10 @@ bool BackendImpl::CreateBackingStore(disk_cache::File* file) {
IndexHeader header;
header.table_len = DesiredIndexTableLen(max_size_);
+ // We need file version 2.1 for the new eviction algorithm.
+ if (new_eviction_)
+ header.version = 0x20001;
+
if (!file->Write(&header, sizeof(header), 0))
return false;
@@ -962,49 +1009,153 @@ bool BackendImpl::OpenFollowingEntry(bool forward, void** iter,
if (disabled_)
return false;
- Rankings::ScopedRankingsBlock rankings(&rankings_,
- reinterpret_cast<CacheRankingsBlock*>(*iter));
- CacheRankingsBlock* next_block = forward ?
- rankings_.GetNext(rankings.get(), Rankings::NO_USE) :
- rankings_.GetPrev(rankings.get(), Rankings::NO_USE);
- Rankings::ScopedRankingsBlock next(&rankings_, next_block);
+ DCHECK(iter);
+ DCHECK(next_entry);
*next_entry = NULL;
+
+ const int kListsToSearch = 3;
+ scoped_refptr<EntryImpl> entries[kListsToSearch];
+ scoped_ptr<Rankings::Iterator> iterator(
+ reinterpret_cast<Rankings::Iterator*>(*iter));
*iter = NULL;
- if (!next.get())
+
+ if (!iterator.get()) {
+ iterator.reset(new Rankings::Iterator(&rankings_));
+ bool ret = false;
+
+ // Get an entry from each list.
+ for (int i = 0; i < kListsToSearch; i++) {
+ EntryImpl* temp = NULL;
+ ret |= OpenFollowingEntryFromList(forward, static_cast<Rankings::List>(i),
+ &iterator->nodes[i], &temp);
+ entries[i].swap(&temp); // The entry was already addref'd.
+ }
+ if (!ret)
+ return false;
+ } else {
+ // Get the next entry from the last list, and the actual entries for the
+ // elements on the other lists.
+ for (int i = 0; i < kListsToSearch; i++) {
+ EntryImpl* temp = NULL;
+ if (iterator->list == i) {
+ OpenFollowingEntryFromList(forward, iterator->list,
+ &iterator->nodes[i], &temp);
+ } else {
+ temp = GetEnumeratedEntry(iterator->nodes[i]);
+ }
+
+ entries[i].swap(&temp); // The entry was already addref'd.
+ }
+ }
+
+ int newest = -1;
+ int oldest = -1;
+ Time access_times[kListsToSearch];
+ for (int i = 0; i < kListsToSearch; i++) {
+ if (entries[i].get()) {
+ access_times[i] = entries[i]->GetLastUsed();
+ if (newest < 0) {
+ DCHECK(oldest < 0);
+ newest = oldest = i;
+ continue;
+ }
+ if (access_times[i] > access_times[newest])
+ newest = i;
+ if (access_times[i] < access_times[oldest])
+ oldest = i;
+ }
+ }
+
+ if (newest < 0 || oldest < 0)
+ return false;
+
+ if (forward) {
+ entries[newest].swap(reinterpret_cast<EntryImpl**>(next_entry));
+ iterator->list = static_cast<Rankings::List>(newest);
+ } else {
+ entries[oldest].swap(reinterpret_cast<EntryImpl**>(next_entry));
+ iterator->list = static_cast<Rankings::List>(oldest);
+ }
+
+ *iter = iterator.release();
+ return true;
+}
+
+bool BackendImpl::OpenFollowingEntryFromList(bool forward, Rankings::List list,
+ CacheRankingsBlock** from_entry,
+ EntryImpl** next_entry) {
+ if (disabled_)
+ return false;
+
+ if (!new_eviction_ && Rankings::NO_USE != list)
+ return false;
+
+ Rankings::ScopedRankingsBlock rankings(&rankings_, *from_entry);
+ CacheRankingsBlock* next_block = forward ?
+ rankings_.GetNext(rankings.get(), list) :
+ rankings_.GetPrev(rankings.get(), list);
+ Rankings::ScopedRankingsBlock next(&rankings_, next_block);
+ *from_entry = NULL;
+
+ *next_entry = GetEnumeratedEntry(next.get());
+ if (!*next_entry)
return false;
+ *from_entry = next.release();
+ return true;
+}
+
+EntryImpl* BackendImpl::GetEnumeratedEntry(CacheRankingsBlock* next) {
+ if (!next)
+ return NULL;
+
scoped_refptr<EntryImpl> entry;
+ EntryImpl* temp = NULL;
+
if (next->Data()->pointer) {
entry = reinterpret_cast<EntryImpl*>(next->Data()->pointer);
- } else {
- bool dirty;
- EntryImpl* temp = NULL;
- if (NewEntry(Addr(next->Data()->contents), &temp, &dirty))
- return false;
entry.swap(&temp);
+ return temp;
+ }
- if (dirty) {
- // We cannot trust this entry. Call MatchEntry to go through the regular
- // path and take the appropriate action.
- std::string key = entry->GetKey();
- uint32 hash = entry->GetHash();
- entry = NULL; // Release the entry.
- temp = MatchEntry(key, hash, false);
- if (temp)
- temp->Release();
+ bool dirty;
+ if (NewEntry(Addr(next->Data()->contents), &temp, &dirty))
+ return NULL;
+ entry.swap(&temp);
+
+ if (dirty) {
+ // We cannot trust this entry. Call MatchEntry to go through the regular
+ // path and take the appropriate action.
+ std::string key = entry->GetKey();
+ uint32 hash = entry->GetHash();
+ entry = NULL; // Release the entry.
+ temp = MatchEntry(key, hash, false);
+ if (temp)
+ temp->Release();
- return false;
- }
+ return NULL;
+ }
- entry.swap(&temp);
- temp = EntryImpl::Update(temp); // Update returns an adref'd entry.
- entry.swap(&temp);
- if (!entry.get())
- return false;
+ entry.swap(&temp);
+ return EntryImpl::Update(temp); // Update returns an adref'd entry.
+}
+
+bool BackendImpl::ResurrectEntry(EntryImpl* deleted_entry, Entry** entry) {
+ if (ENTRY_NORMAL == deleted_entry->entry()->Data()->state) {
+ deleted_entry->Release();
+ stats_.OnEvent(Stats::CREATE_MISS);
+ Trace("create entry miss ");
+ return false;
}
- entry.swap(reinterpret_cast<EntryImpl**>(next_entry));
- *iter = next.release();
+ // We are attempting to create an entry and found out that the entry was
+ // previously deleted.
+
+ eviction_.OnCreateEntry(deleted_entry);
+ *entry = deleted_entry;
+
+ stats_.OnEvent(Stats::CREATE_HIT);
+ Trace("Resurrect entry hit ");
return true;
}
@@ -1017,8 +1168,8 @@ void BackendImpl::DestroyInvalidEntry(Addr address, EntryImpl* entry) {
eviction_.OnDoomEntry(entry);
entry->InternalDoom();
- data_->header.num_entries--;
- DCHECK(data_->header.num_entries >= 0);
+ if (!new_eviction_)
+ DecreaseNumEntries();
stats_.OnEvent(Stats::INVALID_ENTRY);
}
@@ -1049,6 +1200,19 @@ void BackendImpl::DecreaseNumRefs() {
RestartCache();
}
+void BackendImpl::IncreaseNumEntries() {
+ data_->header.num_entries++;
+ DCHECK(data_->header.num_entries > 0);
+}
+
+void BackendImpl::DecreaseNumEntries() {
+ data_->header.num_entries--;
+ if (data_->header.num_entries < 0) {
+ NOTREACHED();
+ data_->header.num_entries = 0;
+ }
+}
+
void BackendImpl::LogStats() {
StatsItems stats;
GetStats(&stats);
@@ -1058,6 +1222,14 @@ void BackendImpl::LogStats() {
}
}
+void BackendImpl::UpgradeTo2_1() {
+ // 2.1 is basically the same as 2.0, except that new fields are actually
+ // updated by the new eviction algorithm.
+ DCHECK(0x20000 == data_->header.version);
+ data_->header.version = 0x20001;
+ data_->header.lru.sizes[Rankings::NO_USE] = data_->header.num_entries;
+}
+
bool BackendImpl::CheckIndex() {
if (!data_) {
LOG(ERROR) << "Unable to map Index file";
@@ -1070,10 +1242,23 @@ bool BackendImpl::CheckIndex() {
return false;
}
- if (kIndexMagic != data_->header.magic ||
- kCurrentVersion != data_->header.version) {
- LOG(ERROR) << "Invalid file version or magic";
- return false;
+ if (new_eviction_) {
+ // We support versions 2.0 and 2.1, upgrading 2.0 to 2.1.
+ if (kIndexMagic != data_->header.magic ||
+ kCurrentVersion >> 16 != data_->header.version >> 16) {
+ LOG(ERROR) << "Invalid file version or magic";
+ return false;
+ }
+ if (kCurrentVersion == data_->header.version) {
+ // We need file version 2.1 for the new eviction algorithm.
+ UpgradeTo2_1();
+ }
+ } else {
+ if (kIndexMagic != data_->header.magic ||
+ kCurrentVersion != data_->header.version) {
+ LOG(ERROR) << "Invalid file version or magic";
+ return false;
+ }
}
if (!data_->header.table_len) {