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 |