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 |