| 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 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 } else { | 154 } else { |
| 155 CACHE_UMA(AGE_MS, "TotalTrimTimeV1", 0, start); | 155 CACHE_UMA(AGE_MS, "TotalTrimTimeV1", 0, start); |
| 156 } | 156 } |
| 157 CACHE_UMA(COUNTS, "TrimItemsV1", 0, deleted_entries); | 157 CACHE_UMA(COUNTS, "TrimItemsV1", 0, deleted_entries); |
| 158 | 158 |
| 159 trimming_ = false; | 159 trimming_ = false; |
| 160 Trace("*** Trim Cache end ***"); | 160 Trace("*** Trim Cache end ***"); |
| 161 return; | 161 return; |
| 162 } | 162 } |
| 163 | 163 |
| 164 void Eviction::UpdateRank(EntryImpl* entry, bool modified) { | 164 void Eviction::OnOpenEntryV2(EntryImpl* entry) { |
| 165 if (new_eviction_) | 165 EntryStore* info = entry->entry()->Data(); |
| 166 return UpdateRankV2(entry, modified); | 166 DCHECK_EQ(ENTRY_NORMAL, info->state); |
| 167 | 167 |
| 168 rankings_->UpdateRank(entry->rankings(), modified, GetListForEntry(entry)); | 168 if (info->reuse_count < kint32max) { |
| 169 info->reuse_count++; |
| 170 entry->entry()->set_modified(); |
| 171 |
| 172 // We may need to move this to a new list. |
| 173 if (1 == info->reuse_count) { |
| 174 rankings_->Remove(entry->rankings(), Rankings::NO_USE, true); |
| 175 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); |
| 176 entry->entry()->Store(); |
| 177 } else if (kHighUse == info->reuse_count) { |
| 178 rankings_->Remove(entry->rankings(), Rankings::LOW_USE, true); |
| 179 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); |
| 180 entry->entry()->Store(); |
| 181 } |
| 182 } |
| 169 } | 183 } |
| 170 | 184 |
| 171 void Eviction::OnOpenEntry(EntryImpl* entry) { | 185 void Eviction::OnCreateEntryV2(EntryImpl* entry) { |
| 172 if (new_eviction_) | 186 EntryStore* info = entry->entry()->Data(); |
| 173 return OnOpenEntryV2(entry); | 187 switch (info->state) { |
| 174 } | 188 case ENTRY_NORMAL: { |
| 189 DCHECK(!info->reuse_count); |
| 190 DCHECK(!info->refetch_count); |
| 191 break; |
| 192 }; |
| 193 case ENTRY_EVICTED: { |
| 194 if (info->refetch_count < kint32max) |
| 195 info->refetch_count++; |
| 175 | 196 |
| 176 void Eviction::OnCreateEntry(EntryImpl* entry) { | 197 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { |
| 177 if (new_eviction_) | 198 info->reuse_count = kHighUse; |
| 178 return OnCreateEntryV2(entry); | 199 } else { |
| 200 info->reuse_count++; |
| 201 } |
| 202 info->state = ENTRY_NORMAL; |
| 203 entry->entry()->Store(); |
| 204 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); |
| 205 break; |
| 206 }; |
| 207 default: |
| 208 NOTREACHED(); |
| 209 } |
| 179 | 210 |
| 180 rankings_->Insert(entry->rankings(), true, GetListForEntry(entry)); | 211 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); |
| 181 } | |
| 182 | |
| 183 void Eviction::OnDoomEntry(EntryImpl* entry) { | |
| 184 if (new_eviction_) | |
| 185 return OnDoomEntryV2(entry); | |
| 186 | |
| 187 if (entry->LeaveRankingsBehind()) | |
| 188 return; | |
| 189 | |
| 190 rankings_->Remove(entry->rankings(), GetListForEntry(entry), true); | |
| 191 } | |
| 192 | |
| 193 void Eviction::OnDestroyEntry(EntryImpl* entry) { | |
| 194 if (new_eviction_) | |
| 195 return OnDestroyEntryV2(entry); | |
| 196 } | 212 } |
| 197 | 213 |
| 198 void Eviction::SetTestMode() { | 214 void Eviction::SetTestMode() { |
| 199 test_mode_ = true; | 215 test_mode_ = true; |
| 200 } | 216 } |
| 201 | 217 |
| 202 void Eviction::TrimDeletedList(bool empty) { | 218 void Eviction::TrimDeletedList(bool empty) { |
| 203 DCHECK(test_mode_ && new_eviction_); | 219 DCHECK(test_mode_ && new_eviction_); |
| 204 TrimDeleted(empty); | 220 TrimDeleted(empty); |
| 205 } | 221 } |
| 206 | 222 |
| 223 // ----------------------------------------------------------------------- |
| 224 |
| 207 void Eviction::PostDelayedTrim() { | 225 void Eviction::PostDelayedTrim() { |
| 208 // Prevent posting multiple tasks. | 226 // Prevent posting multiple tasks. |
| 209 if (delay_trim_) | 227 if (delay_trim_) |
| 210 return; | 228 return; |
| 211 delay_trim_ = true; | 229 delay_trim_ = true; |
| 212 trim_delays_++; | 230 trim_delays_++; |
| 213 base::MessageLoop::current()->PostDelayedTask( | 231 base::MessageLoop::current()->PostDelayedTask( |
| 214 FROM_HERE, | 232 FROM_HERE, |
| 215 base::Bind(&Eviction::DelayedTrim, ptr_factory_.GetWeakPtr()), | 233 base::Bind(&Eviction::DelayedTrim, ptr_factory_.GetWeakPtr()), |
| 216 base::TimeDelta::FromMilliseconds(1000)); | 234 base::TimeDelta::FromMilliseconds(1000)); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 239 int index_load = header_->num_entries * 100 / index_size_; | 257 int index_load = header_->num_entries * 100 / index_size_; |
| 240 | 258 |
| 241 // If the index is not loaded, the deleted list will tend to double the size | 259 // If the index is not loaded, the deleted list will tend to double the size |
| 242 // of the other lists 3 lists (40% of the total). Otherwise, all lists will be | 260 // of the other lists 3 lists (40% of the total). Otherwise, all lists will be |
| 243 // about the same size. | 261 // about the same size. |
| 244 int max_length = (index_load < 25) ? header_->num_entries * 2 / 5 : | 262 int max_length = (index_load < 25) ? header_->num_entries * 2 / 5 : |
| 245 header_->num_entries / 4; | 263 header_->num_entries / 4; |
| 246 return (!test_mode_ && header_->lru.sizes[Rankings::DELETED] > max_length); | 264 return (!test_mode_ && header_->lru.sizes[Rankings::DELETED] > max_length); |
| 247 } | 265 } |
| 248 | 266 |
| 249 void Eviction::ReportTrimTimes(EntryImpl* entry) { | |
| 250 if (first_trim_) { | |
| 251 first_trim_ = false; | |
| 252 if (backend_->ShouldReportAgain()) { | |
| 253 CACHE_UMA(AGE, "TrimAge", 0, entry->GetLastUsed()); | |
| 254 ReportListStats(); | |
| 255 } | |
| 256 | |
| 257 if (header_->lru.filled) | |
| 258 return; | |
| 259 | |
| 260 header_->lru.filled = 1; | |
| 261 | |
| 262 if (header_->create_time) { | |
| 263 // This is the first entry that we have to evict, generate some noise. | |
| 264 backend_->FirstEviction(); | |
| 265 } else { | |
| 266 // This is an old file, but we may want more reports from this user so | |
| 267 // lets save some create_time. | |
| 268 Time::Exploded old = {0}; | |
| 269 old.year = 2009; | |
| 270 old.month = 3; | |
| 271 old.day_of_month = 1; | |
| 272 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | |
| 273 } | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | |
| 278 return Rankings::NO_USE; | |
| 279 } | |
| 280 | |
| 281 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty, | 267 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty, |
| 282 Rankings::List list) { | 268 Rankings::List list) { |
| 283 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); | 269 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); |
| 284 if (!entry) { | 270 if (!entry) { |
| 285 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 271 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
| 286 return false; | 272 return false; |
| 287 } | 273 } |
| 288 | 274 |
| 289 ReportTrimTimes(entry); | 275 ReportTrimTimes(entry); |
| 290 if (empty || !new_eviction_) { | 276 if (empty || !new_eviction_) { |
| 291 entry->DoomImpl(); | 277 entry->DoomImpl(); |
| 292 } else { | 278 } else { |
| 293 entry->DeleteEntryData(false); | 279 entry->DeleteEntryData(false); |
| 294 EntryStore* info = entry->entry()->Data(); | 280 EntryStore* info = entry->entry()->Data(); |
| 295 DCHECK_EQ(ENTRY_NORMAL, info->state); | 281 DCHECK_EQ(ENTRY_NORMAL, info->state); |
| 296 | 282 |
| 297 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); | 283 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); |
| 298 info->state = ENTRY_EVICTED; | 284 info->state = ENTRY_EVICTED; |
| 299 entry->entry()->Store(); | 285 entry->entry()->Store(); |
| 300 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 286 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
| 301 } | 287 } |
| 302 if (!empty) | 288 if (!empty) |
| 303 backend_->OnEvent(Stats::TRIM_ENTRY); | 289 backend_->OnEvent(Stats::TRIM_ENTRY); |
| 304 | 290 |
| 305 entry->Release(); | 291 entry->Release(); |
| 306 | 292 |
| 307 return true; | 293 return true; |
| 308 } | 294 } |
| 309 | 295 |
| 310 // ----------------------------------------------------------------------- | |
| 311 | |
| 312 void Eviction::TrimCacheV2(bool empty) { | 296 void Eviction::TrimCacheV2(bool empty) { |
| 313 Trace("*** Trim Cache ***"); | 297 Trace("*** Trim Cache ***"); |
| 314 trimming_ = true; | 298 trimming_ = true; |
| 315 TimeTicks start = TimeTicks::Now(); | 299 TimeTicks start = TimeTicks::Now(); |
| 316 | 300 |
| 317 const int kListsToSearch = 3; | 301 const int kListsToSearch = 3; |
| 318 Rankings::ScopedRankingsBlock next[kListsToSearch]; | 302 Rankings::ScopedRankingsBlock next[kListsToSearch]; |
| 319 int list = Rankings::LAST_ELEMENT; | 303 int list = Rankings::LAST_ELEMENT; |
| 320 | 304 |
| 321 // Get a node from each list. | 305 // Get a node from each list. |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 } else { | 370 } else { |
| 387 CACHE_UMA(AGE_MS, "TotalTrimTimeV2", 0, start); | 371 CACHE_UMA(AGE_MS, "TotalTrimTimeV2", 0, start); |
| 388 } | 372 } |
| 389 CACHE_UMA(COUNTS, "TrimItemsV2", 0, deleted_entries); | 373 CACHE_UMA(COUNTS, "TrimItemsV2", 0, deleted_entries); |
| 390 | 374 |
| 391 Trace("*** Trim Cache end ***"); | 375 Trace("*** Trim Cache end ***"); |
| 392 trimming_ = false; | 376 trimming_ = false; |
| 393 return; | 377 return; |
| 394 } | 378 } |
| 395 | 379 |
| 396 void Eviction::UpdateRankV2(EntryImpl* entry, bool modified) { | |
| 397 rankings_->UpdateRank(entry->rankings(), modified, GetListForEntryV2(entry)); | |
| 398 } | |
| 399 | |
| 400 void Eviction::OnOpenEntryV2(EntryImpl* entry) { | |
| 401 EntryStore* info = entry->entry()->Data(); | |
| 402 DCHECK_EQ(ENTRY_NORMAL, info->state); | |
| 403 | |
| 404 if (info->reuse_count < kint32max) { | |
| 405 info->reuse_count++; | |
| 406 entry->entry()->set_modified(); | |
| 407 | |
| 408 // We may need to move this to a new list. | |
| 409 if (1 == info->reuse_count) { | |
| 410 rankings_->Remove(entry->rankings(), Rankings::NO_USE, true); | |
| 411 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); | |
| 412 entry->entry()->Store(); | |
| 413 } else if (kHighUse == info->reuse_count) { | |
| 414 rankings_->Remove(entry->rankings(), Rankings::LOW_USE, true); | |
| 415 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); | |
| 416 entry->entry()->Store(); | |
| 417 } | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 void Eviction::OnCreateEntryV2(EntryImpl* entry) { | |
| 422 EntryStore* info = entry->entry()->Data(); | |
| 423 switch (info->state) { | |
| 424 case ENTRY_NORMAL: { | |
| 425 DCHECK(!info->reuse_count); | |
| 426 DCHECK(!info->refetch_count); | |
| 427 break; | |
| 428 }; | |
| 429 case ENTRY_EVICTED: { | |
| 430 if (info->refetch_count < kint32max) | |
| 431 info->refetch_count++; | |
| 432 | |
| 433 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { | |
| 434 info->reuse_count = kHighUse; | |
| 435 } else { | |
| 436 info->reuse_count++; | |
| 437 } | |
| 438 info->state = ENTRY_NORMAL; | |
| 439 entry->entry()->Store(); | |
| 440 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); | |
| 441 break; | |
| 442 }; | |
| 443 default: | |
| 444 NOTREACHED(); | |
| 445 } | |
| 446 | |
| 447 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); | |
| 448 } | |
| 449 | |
| 450 void Eviction::OnDoomEntryV2(EntryImpl* entry) { | |
| 451 EntryStore* info = entry->entry()->Data(); | |
| 452 if (ENTRY_NORMAL != info->state) | |
| 453 return; | |
| 454 | |
| 455 if (entry->LeaveRankingsBehind()) { | |
| 456 info->state = ENTRY_DOOMED; | |
| 457 entry->entry()->Store(); | |
| 458 return; | |
| 459 } | |
| 460 | |
| 461 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); | |
| 462 | |
| 463 info->state = ENTRY_DOOMED; | |
| 464 entry->entry()->Store(); | |
| 465 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | |
| 466 } | |
| 467 | |
| 468 void Eviction::OnDestroyEntryV2(EntryImpl* entry) { | |
| 469 if (entry->LeaveRankingsBehind()) | |
| 470 return; | |
| 471 | |
| 472 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); | |
| 473 } | |
| 474 | |
| 475 Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) { | |
| 476 EntryStore* info = entry->entry()->Data(); | |
| 477 DCHECK_EQ(ENTRY_NORMAL, info->state); | |
| 478 | |
| 479 if (!info->reuse_count) | |
| 480 return Rankings::NO_USE; | |
| 481 | |
| 482 if (info->reuse_count < kHighUse) | |
| 483 return Rankings::LOW_USE; | |
| 484 | |
| 485 return Rankings::HIGH_USE; | |
| 486 } | |
| 487 | |
| 488 // This is a minimal implementation that just discards the oldest nodes. | 380 // This is a minimal implementation that just discards the oldest nodes. |
| 489 // TODO(rvargas): Do something better here. | 381 // TODO(rvargas): Do something better here. |
| 490 void Eviction::TrimDeleted(bool empty) { | 382 void Eviction::TrimDeleted(bool empty) { |
| 491 Trace("*** Trim Deleted ***"); | 383 Trace("*** Trim Deleted ***"); |
| 492 if (backend_->disabled_) | 384 if (backend_->disabled_) |
| 493 return; | 385 return; |
| 494 | 386 |
| 495 TimeTicks start = TimeTicks::Now(); | 387 TimeTicks start = TimeTicks::Now(); |
| 496 Rankings::ScopedRankingsBlock node(rankings_); | 388 Rankings::ScopedRankingsBlock node(rankings_); |
| 497 Rankings::ScopedRankingsBlock next( | 389 Rankings::ScopedRankingsBlock next( |
| (...skipping 15 matching lines...) Expand all Loading... |
| 513 FROM_HERE, | 405 FROM_HERE, |
| 514 base::Bind(&Eviction::TrimDeleted, ptr_factory_.GetWeakPtr(), false)); | 406 base::Bind(&Eviction::TrimDeleted, ptr_factory_.GetWeakPtr(), false)); |
| 515 } | 407 } |
| 516 | 408 |
| 517 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); | 409 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); |
| 518 CACHE_UMA(COUNTS, "TrimDeletedItems", 0, deleted_entries); | 410 CACHE_UMA(COUNTS, "TrimDeletedItems", 0, deleted_entries); |
| 519 Trace("*** Trim Deleted end ***"); | 411 Trace("*** Trim Deleted end ***"); |
| 520 return; | 412 return; |
| 521 } | 413 } |
| 522 | 414 |
| 523 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { | 415 void Eviction::ReportTrimTimes(EntryImpl* entry) { |
| 524 EntryImpl* entry = backend_->GetEnumeratedEntry(node, Rankings::DELETED); | 416 if (first_trim_) { |
| 525 if (!entry) { | 417 first_trim_ = false; |
| 526 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 418 if (backend_->ShouldReportAgain()) { |
| 527 return false; | 419 CACHE_UMA(AGE, "TrimAge", 0, entry->GetLastUsed()); |
| 420 ReportListStats(); |
| 421 } |
| 422 |
| 423 if (header_->lru.filled) |
| 424 return; |
| 425 |
| 426 header_->lru.filled = 1; |
| 427 |
| 428 if (header_->create_time) { |
| 429 // This is the first entry that we have to evict, generate some noise. |
| 430 backend_->FirstEviction(); |
| 431 } else { |
| 432 // This is an old file, but we may want more reports from this user so |
| 433 // lets save some create_time. |
| 434 Time::Exploded old = {0}; |
| 435 old.year = 2009; |
| 436 old.month = 3; |
| 437 old.day_of_month = 1; |
| 438 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
| 439 } |
| 528 } | 440 } |
| 529 | |
| 530 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); | |
| 531 entry->entry()->Data()->state = ENTRY_DOOMED; | |
| 532 entry->DoomImpl(); | |
| 533 entry->Release(); | |
| 534 return !doomed; | |
| 535 } | 441 } |
| 536 | 442 |
| 537 bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) { | 443 bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) { |
| 538 if (!node) | 444 if (!node) |
| 539 return false; | 445 return false; |
| 540 | 446 |
| 541 // If possible, we want to keep entries on each list at least kTargetTime | 447 // If possible, we want to keep entries on each list at least kTargetTime |
| 542 // hours. Each successive list on the enumeration has 2x the target time of | 448 // hours. Each successive list on the enumeration has 2x the target time of |
| 543 // the previous list. | 449 // the previous list. |
| 544 Time used = Time::FromInternalValue(node->Data()->last_used); | 450 Time used = Time::FromInternalValue(node->Data()->last_used); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 587 Time::FromInternalValue(last2.get()->Data()->last_used)); | 493 Time::FromInternalValue(last2.get()->Data()->last_used)); |
| 588 if (last3.get()) | 494 if (last3.get()) |
| 589 CACHE_UMA(AGE, "HighUseAge", 0, | 495 CACHE_UMA(AGE, "HighUseAge", 0, |
| 590 Time::FromInternalValue(last3.get()->Data()->last_used)); | 496 Time::FromInternalValue(last3.get()->Data()->last_used)); |
| 591 if (last4.get()) | 497 if (last4.get()) |
| 592 CACHE_UMA(AGE, "DeletedAge", 0, | 498 CACHE_UMA(AGE, "DeletedAge", 0, |
| 593 Time::FromInternalValue(last4.get()->Data()->last_used)); | 499 Time::FromInternalValue(last4.get()->Data()->last_used)); |
| 594 } | 500 } |
| 595 | 501 |
| 596 } // namespace disk_cache | 502 } // namespace disk_cache |
| OLD | NEW |