| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // The eviction policy is a very simple pure LRU, so the elements at the end of | 5 // The eviction policy is a very simple pure LRU, so the elements at the end of |
| 6 // the list are evicted until kCleanUpMargin free space is available. There is | 6 // the list are evicted until kCleanUpMargin free space is available. There is |
| 7 // only one list in use (Rankings::NO_USE), and elements are sent to the front | 7 // only one list in use (Rankings::NO_USE), and elements are sent to the front |
| 8 // of the list whenever they are accessed. | 8 // of the list whenever they are accessed. |
| 9 | 9 |
| 10 // The new (in-development) eviction policy adds re-use as a factor to evict | 10 // The new (in-development) eviction policy adds re-use as a factor to evict |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 | 43 |
| 44 // Provide a BackendImpl object to macros from histogram_macros.h. | 44 // Provide a BackendImpl object to macros from histogram_macros.h. |
| 45 #define CACHE_UMA_BACKEND_IMPL_OBJ backend_ | 45 #define CACHE_UMA_BACKEND_IMPL_OBJ backend_ |
| 46 | 46 |
| 47 using base::Time; | 47 using base::Time; |
| 48 using base::TimeTicks; | 48 using base::TimeTicks; |
| 49 | 49 |
| 50 namespace { | 50 namespace { |
| 51 | 51 |
| 52 const int kCleanUpMargin = 1024 * 1024; | 52 const int kCleanUpMargin = 1024 * 1024; |
| 53 const int kHighUse = 10; // Reuse count to be on the HIGH_USE list. | 53 const int kHighUse = 10; // Reuse count to be on the HIGH_USE list. |
| 54 const int kTargetTime = 24 * 7; // Time to be evicted (hours since last use). | 54 const int kTargetTime = 24 * 7; // Time to be evicted (hours since last use). |
| 55 const int kMaxDelayedTrims = 60; | 55 const int kMaxDelayedTrims = 60; |
| 56 | 56 |
| 57 int LowWaterAdjust(int high_water) { | 57 int LowWaterAdjust(int high_water) { |
| 58 if (high_water < kCleanUpMargin) | 58 if (high_water < kCleanUpMargin) |
| 59 return 0; | 59 return 0; |
| 60 | 60 |
| 61 return high_water - kCleanUpMargin; | 61 return high_water - kCleanUpMargin; |
| 62 } | 62 } |
| 63 | 63 |
| 64 bool FallingBehind(int current_size, int max_size) { | 64 bool FallingBehind(int current_size, int max_size) { |
| 65 return current_size > max_size - kCleanUpMargin * 20; | 65 return current_size > max_size - kCleanUpMargin * 20; |
| 66 } | 66 } |
| 67 | 67 |
| 68 } // namespace | 68 } // namespace |
| 69 | 69 |
| 70 namespace disk_cache { | 70 namespace disk_cache { |
| 71 | 71 |
| 72 // The real initialization happens during Init(), init_ is the only member that | 72 // The real initialization happens during Init(), init_ is the only member that |
| 73 // has to be initialized here. | 73 // has to be initialized here. |
| 74 Eviction::Eviction() | 74 Eviction::Eviction() : backend_(NULL), init_(false), ptr_factory_(this) { |
| 75 : backend_(NULL), | |
| 76 init_(false), | |
| 77 ptr_factory_(this) { | |
| 78 } | 75 } |
| 79 | 76 |
| 80 Eviction::~Eviction() { | 77 Eviction::~Eviction() { |
| 81 } | 78 } |
| 82 | 79 |
| 83 void Eviction::Init(BackendImpl* backend) { | 80 void Eviction::Init(BackendImpl* backend) { |
| 84 // We grab a bunch of info from the backend to make the code a little cleaner | 81 // We grab a bunch of info from the backend to make the code a little cleaner |
| 85 // when we're actually doing work. | 82 // when we're actually doing work. |
| 86 backend_ = backend; | 83 backend_ = backend; |
| 87 rankings_ = &backend->rankings_; | 84 rankings_ = &backend->rankings_; |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 trim_delays_ = 0; | 235 trim_delays_ = 0; |
| 239 return true; | 236 return true; |
| 240 } | 237 } |
| 241 | 238 |
| 242 bool Eviction::ShouldTrimDeleted() { | 239 bool Eviction::ShouldTrimDeleted() { |
| 243 int index_load = header_->num_entries * 100 / index_size_; | 240 int index_load = header_->num_entries * 100 / index_size_; |
| 244 | 241 |
| 245 // If the index is not loaded, the deleted list will tend to double the size | 242 // If the index is not loaded, the deleted list will tend to double the size |
| 246 // of the other lists 3 lists (40% of the total). Otherwise, all lists will be | 243 // of the other lists 3 lists (40% of the total). Otherwise, all lists will be |
| 247 // about the same size. | 244 // about the same size. |
| 248 int max_length = (index_load < 25) ? header_->num_entries * 2 / 5 : | 245 int max_length = (index_load < 25) ? header_->num_entries * 2 / 5 |
| 249 header_->num_entries / 4; | 246 : header_->num_entries / 4; |
| 250 return (!test_mode_ && header_->lru.sizes[Rankings::DELETED] > max_length); | 247 return (!test_mode_ && header_->lru.sizes[Rankings::DELETED] > max_length); |
| 251 } | 248 } |
| 252 | 249 |
| 253 void Eviction::ReportTrimTimes(EntryImpl* entry) { | 250 void Eviction::ReportTrimTimes(EntryImpl* entry) { |
| 254 if (first_trim_) { | 251 if (first_trim_) { |
| 255 first_trim_ = false; | 252 first_trim_ = false; |
| 256 if (backend_->ShouldReportAgain()) { | 253 if (backend_->ShouldReportAgain()) { |
| 257 CACHE_UMA(AGE, "TrimAge", 0, entry->GetLastUsed()); | 254 CACHE_UMA(AGE, "TrimAge", 0, entry->GetLastUsed()); |
| 258 ReportListStats(); | 255 ReportListStats(); |
| 259 } | 256 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 275 old.day_of_month = 1; | 272 old.day_of_month = 1; |
| 276 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | 273 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
| 277 } | 274 } |
| 278 } | 275 } |
| 279 } | 276 } |
| 280 | 277 |
| 281 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | 278 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { |
| 282 return Rankings::NO_USE; | 279 return Rankings::NO_USE; |
| 283 } | 280 } |
| 284 | 281 |
| 285 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty, | 282 bool Eviction::EvictEntry(CacheRankingsBlock* node, |
| 283 bool empty, |
| 286 Rankings::List list) { | 284 Rankings::List list) { |
| 287 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); | 285 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); |
| 288 if (!entry) { | 286 if (!entry) { |
| 289 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 287 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
| 290 return false; | 288 return false; |
| 291 } | 289 } |
| 292 | 290 |
| 293 ReportTrimTimes(entry); | 291 ReportTrimTimes(entry); |
| 294 if (empty || !new_eviction_) { | 292 if (empty || !new_eviction_) { |
| 295 entry->DoomImpl(); | 293 entry->DoomImpl(); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 int deleted_entries = 0; | 344 int deleted_entries = 0; |
| 347 int target_size = empty ? 0 : max_size_; | 345 int target_size = empty ? 0 : max_size_; |
| 348 | 346 |
| 349 for (; list < kListsToSearch; list++) { | 347 for (; list < kListsToSearch; list++) { |
| 350 while ((header_->num_bytes > target_size || test_mode_) && | 348 while ((header_->num_bytes > target_size || test_mode_) && |
| 351 next[list].get()) { | 349 next[list].get()) { |
| 352 // The iterator could be invalidated within EvictEntry(). | 350 // The iterator could be invalidated within EvictEntry(). |
| 353 if (!next[list]->HasData()) | 351 if (!next[list]->HasData()) |
| 354 break; | 352 break; |
| 355 node.reset(next[list].release()); | 353 node.reset(next[list].release()); |
| 356 next[list].reset(rankings_->GetPrev(node.get(), | 354 next[list].reset( |
| 357 static_cast<Rankings::List>(list))); | 355 rankings_->GetPrev(node.get(), static_cast<Rankings::List>(list))); |
| 358 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 356 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
| 359 // This entry is not being used by anybody. | 357 // This entry is not being used by anybody. |
| 360 // Do NOT use node as an iterator after this point. | 358 // Do NOT use node as an iterator after this point. |
| 361 rankings_->TrackRankingsBlock(node.get(), false); | 359 rankings_->TrackRankingsBlock(node.get(), false); |
| 362 if (EvictEntry(node.get(), empty, static_cast<Rankings::List>(list))) | 360 if (EvictEntry(node.get(), empty, static_cast<Rankings::List>(list))) |
| 363 deleted_entries++; | 361 deleted_entries++; |
| 364 | 362 |
| 365 if (!empty && test_mode_) | 363 if (!empty && test_mode_) |
| 366 break; | 364 break; |
| 367 } | 365 } |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 492 // This is a minimal implementation that just discards the oldest nodes. | 490 // This is a minimal implementation that just discards the oldest nodes. |
| 493 // TODO(rvargas): Do something better here. | 491 // TODO(rvargas): Do something better here. |
| 494 void Eviction::TrimDeleted(bool empty) { | 492 void Eviction::TrimDeleted(bool empty) { |
| 495 Trace("*** Trim Deleted ***"); | 493 Trace("*** Trim Deleted ***"); |
| 496 if (backend_->disabled_) | 494 if (backend_->disabled_) |
| 497 return; | 495 return; |
| 498 | 496 |
| 499 TimeTicks start = TimeTicks::Now(); | 497 TimeTicks start = TimeTicks::Now(); |
| 500 Rankings::ScopedRankingsBlock node(rankings_); | 498 Rankings::ScopedRankingsBlock node(rankings_); |
| 501 Rankings::ScopedRankingsBlock next( | 499 Rankings::ScopedRankingsBlock next( |
| 502 rankings_, rankings_->GetPrev(node.get(), Rankings::DELETED)); | 500 rankings_, rankings_->GetPrev(node.get(), Rankings::DELETED)); |
| 503 int deleted_entries = 0; | 501 int deleted_entries = 0; |
| 504 while (next.get() && | 502 while (next.get() && |
| 505 (empty || (deleted_entries < 20 && | 503 (empty || (deleted_entries < 20 && |
| 506 (TimeTicks::Now() - start).InMilliseconds() < 20))) { | 504 (TimeTicks::Now() - start).InMilliseconds() < 20))) { |
| 507 node.reset(next.release()); | 505 node.reset(next.release()); |
| 508 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); | 506 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); |
| 509 if (RemoveDeletedNode(node.get())) | 507 if (RemoveDeletedNode(node.get())) |
| 510 deleted_entries++; | 508 deleted_entries++; |
| 511 if (test_mode_) | 509 if (test_mode_) |
| 512 break; | 510 break; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 544 | 542 |
| 545 // If possible, we want to keep entries on each list at least kTargetTime | 543 // If possible, we want to keep entries on each list at least kTargetTime |
| 546 // hours. Each successive list on the enumeration has 2x the target time of | 544 // hours. Each successive list on the enumeration has 2x the target time of |
| 547 // the previous list. | 545 // the previous list. |
| 548 Time used = Time::FromInternalValue(node->Data()->last_used); | 546 Time used = Time::FromInternalValue(node->Data()->last_used); |
| 549 int multiplier = 1 << list; | 547 int multiplier = 1 << list; |
| 550 return (Time::Now() - used).InHours() > kTargetTime * multiplier; | 548 return (Time::Now() - used).InHours() > kTargetTime * multiplier; |
| 551 } | 549 } |
| 552 | 550 |
| 553 int Eviction::SelectListByLength(Rankings::ScopedRankingsBlock* next) { | 551 int Eviction::SelectListByLength(Rankings::ScopedRankingsBlock* next) { |
| 554 int data_entries = header_->num_entries - | 552 int data_entries = |
| 555 header_->lru.sizes[Rankings::DELETED]; | 553 header_->num_entries - header_->lru.sizes[Rankings::DELETED]; |
| 556 // Start by having each list to be roughly the same size. | 554 // Start by having each list to be roughly the same size. |
| 557 if (header_->lru.sizes[0] > data_entries / 3) | 555 if (header_->lru.sizes[0] > data_entries / 3) |
| 558 return 0; | 556 return 0; |
| 559 | 557 |
| 560 int list = (header_->lru.sizes[1] > data_entries / 3) ? 1 : 2; | 558 int list = (header_->lru.sizes[1] > data_entries / 3) ? 1 : 2; |
| 561 | 559 |
| 562 // Make sure that frequently used items are kept for a minimum time; we know | 560 // Make sure that frequently used items are kept for a minimum time; we know |
| 563 // that this entry is not older than its current target, but it must be at | 561 // that this entry is not older than its current target, but it must be at |
| 564 // least older than the target for list 0 (kTargetTime), as long as we don't | 562 // least older than the target for list 0 (kTargetTime), as long as we don't |
| 565 // exhaust list 0. | 563 // exhaust list 0. |
| 566 if (!NodeIsOldEnough(next[list].get(), 0) && | 564 if (!NodeIsOldEnough(next[list].get(), 0) && |
| 567 header_->lru.sizes[0] > data_entries / 10) | 565 header_->lru.sizes[0] > data_entries / 10) |
| 568 list = 0; | 566 list = 0; |
| 569 | 567 |
| 570 return list; | 568 return list; |
| 571 } | 569 } |
| 572 | 570 |
| 573 void Eviction::ReportListStats() { | 571 void Eviction::ReportListStats() { |
| 574 if (!new_eviction_) | 572 if (!new_eviction_) |
| 575 return; | 573 return; |
| 576 | 574 |
| 577 Rankings::ScopedRankingsBlock last1(rankings_, | 575 Rankings::ScopedRankingsBlock last1( |
| 578 rankings_->GetPrev(NULL, Rankings::NO_USE)); | 576 rankings_, rankings_->GetPrev(NULL, Rankings::NO_USE)); |
| 579 Rankings::ScopedRankingsBlock last2(rankings_, | 577 Rankings::ScopedRankingsBlock last2( |
| 580 rankings_->GetPrev(NULL, Rankings::LOW_USE)); | 578 rankings_, rankings_->GetPrev(NULL, Rankings::LOW_USE)); |
| 581 Rankings::ScopedRankingsBlock last3(rankings_, | 579 Rankings::ScopedRankingsBlock last3( |
| 582 rankings_->GetPrev(NULL, Rankings::HIGH_USE)); | 580 rankings_, rankings_->GetPrev(NULL, Rankings::HIGH_USE)); |
| 583 Rankings::ScopedRankingsBlock last4(rankings_, | 581 Rankings::ScopedRankingsBlock last4( |
| 584 rankings_->GetPrev(NULL, Rankings::DELETED)); | 582 rankings_, rankings_->GetPrev(NULL, Rankings::DELETED)); |
| 585 | 583 |
| 586 if (last1.get()) | 584 if (last1.get()) |
| 587 CACHE_UMA(AGE, "NoUseAge", 0, | 585 CACHE_UMA(AGE, |
| 586 "NoUseAge", |
| 587 0, |
| 588 Time::FromInternalValue(last1.get()->Data()->last_used)); | 588 Time::FromInternalValue(last1.get()->Data()->last_used)); |
| 589 if (last2.get()) | 589 if (last2.get()) |
| 590 CACHE_UMA(AGE, "LowUseAge", 0, | 590 CACHE_UMA(AGE, |
| 591 "LowUseAge", |
| 592 0, |
| 591 Time::FromInternalValue(last2.get()->Data()->last_used)); | 593 Time::FromInternalValue(last2.get()->Data()->last_used)); |
| 592 if (last3.get()) | 594 if (last3.get()) |
| 593 CACHE_UMA(AGE, "HighUseAge", 0, | 595 CACHE_UMA(AGE, |
| 596 "HighUseAge", |
| 597 0, |
| 594 Time::FromInternalValue(last3.get()->Data()->last_used)); | 598 Time::FromInternalValue(last3.get()->Data()->last_used)); |
| 595 if (last4.get()) | 599 if (last4.get()) |
| 596 CACHE_UMA(AGE, "DeletedAge", 0, | 600 CACHE_UMA(AGE, |
| 601 "DeletedAge", |
| 602 0, |
| 597 Time::FromInternalValue(last4.get()->Data()->last_used)); | 603 Time::FromInternalValue(last4.get()->Data()->last_used)); |
| 598 } | 604 } |
| 599 | 605 |
| 600 } // namespace disk_cache | 606 } // namespace disk_cache |
| OLD | NEW |