OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 if (new_eviction_) | 109 if (new_eviction_) |
110 return TrimCacheV2(empty); | 110 return TrimCacheV2(empty); |
111 | 111 |
112 Trace("*** Trim Cache ***"); | 112 Trace("*** Trim Cache ***"); |
113 trimming_ = true; | 113 trimming_ = true; |
114 TimeTicks start = TimeTicks::Now(); | 114 TimeTicks start = TimeTicks::Now(); |
115 Rankings::ScopedRankingsBlock node(rankings_); | 115 Rankings::ScopedRankingsBlock node(rankings_); |
116 Rankings::ScopedRankingsBlock next(rankings_, | 116 Rankings::ScopedRankingsBlock next(rankings_, |
117 rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 117 rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
118 int target_size = empty ? 0 : max_size_; | 118 int target_size = empty ? 0 : max_size_; |
119 while ((header_->num_bytes > target_size && next.get()) || test_mode_) { | 119 while ((header_->num_bytes > target_size || test_mode_) && next.get()) { |
120 // The iterator could be invalidated within EvictEntry(). | 120 // The iterator could be invalidated within EvictEntry(). |
121 if (!next->HasData()) | 121 if (!next->HasData()) |
122 break; | 122 break; |
123 node.reset(next.release()); | 123 node.reset(next.release()); |
124 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); | 124 next.reset(rankings_->GetPrev(node.get(), Rankings::NO_USE)); |
125 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 125 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
126 // This entry is not being used by anybody. | 126 // This entry is not being used by anybody. |
127 // Do NOT use node as an iterator after this point. | 127 // Do NOT use node as an iterator after this point. |
128 rankings_->TrackRankingsBlock(node.get(), false); | 128 rankings_->TrackRankingsBlock(node.get(), false); |
129 if (!EvictEntry(node.get(), empty) && !test_mode_) | 129 if (!EvictEntry(node.get(), empty, Rankings::NO_USE) && !test_mode_) |
130 continue; | 130 continue; |
131 | 131 |
132 if (!empty) { | 132 if (!empty) { |
133 backend_->OnEvent(Stats::TRIM_ENTRY); | 133 backend_->OnEvent(Stats::TRIM_ENTRY); |
134 if (test_mode_) | 134 if (test_mode_) |
135 break; | 135 break; |
136 | 136 |
137 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { | 137 if ((TimeTicks::Now() - start).InMilliseconds() > 20) { |
138 MessageLoop::current()->PostTask(FROM_HERE, | 138 MessageLoop::current()->PostTask(FROM_HERE, |
139 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 139 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
(...skipping 30 matching lines...) Expand all Loading... |
170 if (new_eviction_) | 170 if (new_eviction_) |
171 return OnCreateEntryV2(entry); | 171 return OnCreateEntryV2(entry); |
172 | 172 |
173 rankings_->Insert(entry->rankings(), true, GetListForEntry(entry)); | 173 rankings_->Insert(entry->rankings(), true, GetListForEntry(entry)); |
174 } | 174 } |
175 | 175 |
176 void Eviction::OnDoomEntry(EntryImpl* entry) { | 176 void Eviction::OnDoomEntry(EntryImpl* entry) { |
177 if (new_eviction_) | 177 if (new_eviction_) |
178 return OnDoomEntryV2(entry); | 178 return OnDoomEntryV2(entry); |
179 | 179 |
180 rankings_->Remove(entry->rankings(), GetListForEntry(entry)); | 180 if (entry->LeaveRankingsBehind()) |
| 181 return; |
| 182 |
| 183 rankings_->Remove(entry->rankings(), GetListForEntry(entry), true); |
181 } | 184 } |
182 | 185 |
183 void Eviction::OnDestroyEntry(EntryImpl* entry) { | 186 void Eviction::OnDestroyEntry(EntryImpl* entry) { |
184 if (new_eviction_) | 187 if (new_eviction_) |
185 return OnDestroyEntryV2(entry); | 188 return OnDestroyEntryV2(entry); |
186 } | 189 } |
187 | 190 |
188 void Eviction::SetTestMode() { | 191 void Eviction::SetTestMode() { |
189 test_mode_ = true; | 192 test_mode_ = true; |
190 } | 193 } |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 old.day_of_month = 1; | 250 old.day_of_month = 1; |
248 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); | 251 header_->create_time = Time::FromLocalExploded(old).ToInternalValue(); |
249 } | 252 } |
250 } | 253 } |
251 } | 254 } |
252 | 255 |
253 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { | 256 Rankings::List Eviction::GetListForEntry(EntryImpl* entry) { |
254 return Rankings::NO_USE; | 257 return Rankings::NO_USE; |
255 } | 258 } |
256 | 259 |
257 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty) { | 260 bool Eviction::EvictEntry(CacheRankingsBlock* node, bool empty, |
258 EntryImpl* entry = backend_->GetEnumeratedEntry(node); | 261 Rankings::List list) { |
| 262 EntryImpl* entry = backend_->GetEnumeratedEntry(node, list); |
259 if (!entry) { | 263 if (!entry) { |
260 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 264 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
261 return false; | 265 return false; |
262 } | 266 } |
263 | 267 |
264 ReportTrimTimes(entry); | 268 ReportTrimTimes(entry); |
265 if (empty || !new_eviction_) { | 269 if (empty || !new_eviction_) { |
266 entry->DoomImpl(); | 270 entry->DoomImpl(); |
267 } else { | 271 } else { |
268 entry->DeleteEntryData(false); | 272 entry->DeleteEntryData(false); |
269 EntryStore* info = entry->entry()->Data(); | 273 EntryStore* info = entry->entry()->Data(); |
270 DCHECK_EQ(ENTRY_NORMAL, info->state); | 274 DCHECK_EQ(ENTRY_NORMAL, info->state); |
271 | 275 |
272 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); | 276 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); |
273 info->state = ENTRY_EVICTED; | 277 info->state = ENTRY_EVICTED; |
274 entry->entry()->Store(); | 278 entry->entry()->Store(); |
275 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 279 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
276 backend_->OnEvent(Stats::TRIM_ENTRY); | 280 backend_->OnEvent(Stats::TRIM_ENTRY); |
277 } | 281 } |
278 entry->Release(); | 282 entry->Release(); |
279 | 283 |
280 return true; | 284 return true; |
281 } | 285 } |
282 | 286 |
(...skipping 25 matching lines...) Expand all Loading... |
308 if (!empty && Rankings::LAST_ELEMENT == list) | 312 if (!empty && Rankings::LAST_ELEMENT == list) |
309 list = SelectListByLength(next); | 313 list = SelectListByLength(next); |
310 | 314 |
311 if (empty) | 315 if (empty) |
312 list = 0; | 316 list = 0; |
313 | 317 |
314 Rankings::ScopedRankingsBlock node(rankings_); | 318 Rankings::ScopedRankingsBlock node(rankings_); |
315 | 319 |
316 int target_size = empty ? 0 : max_size_; | 320 int target_size = empty ? 0 : max_size_; |
317 for (; list < kListsToSearch; list++) { | 321 for (; list < kListsToSearch; list++) { |
318 while ((header_->num_bytes > target_size && next[list].get()) || | 322 while ((header_->num_bytes > target_size || test_mode_) && |
319 test_mode_) { | 323 next[list].get()) { |
320 // The iterator could be invalidated within EvictEntry(). | 324 // The iterator could be invalidated within EvictEntry(). |
321 if (!next[list]->HasData()) | 325 if (!next[list]->HasData()) |
322 break; | 326 break; |
323 node.reset(next[list].release()); | 327 node.reset(next[list].release()); |
324 next[list].reset(rankings_->GetPrev(node.get(), | 328 next[list].reset(rankings_->GetPrev(node.get(), |
325 static_cast<Rankings::List>(list))); | 329 static_cast<Rankings::List>(list))); |
326 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { | 330 if (node->Data()->dirty != backend_->GetCurrentEntryId() || empty) { |
327 // This entry is not being used by anybody. | 331 // This entry is not being used by anybody. |
328 // Do NOT use node as an iterator after this point. | 332 // Do NOT use node as an iterator after this point. |
329 rankings_->TrackRankingsBlock(node.get(), false); | 333 rankings_->TrackRankingsBlock(node.get(), false); |
330 if (!EvictEntry(node.get(), empty) && !test_mode_) | 334 if (!EvictEntry(node.get(), empty, static_cast<Rankings::List>(list)) && |
| 335 !test_mode_) |
331 continue; | 336 continue; |
332 | 337 |
333 if (!empty && test_mode_) | 338 if (!empty && test_mode_) |
334 break; | 339 break; |
335 | 340 |
336 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { | 341 if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) { |
337 MessageLoop::current()->PostTask(FROM_HERE, | 342 MessageLoop::current()->PostTask(FROM_HERE, |
338 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); | 343 factory_.NewRunnableMethod(&Eviction::TrimCache, false)); |
339 break; | 344 break; |
340 } | 345 } |
(...skipping 29 matching lines...) Expand all Loading... |
370 void Eviction::OnOpenEntryV2(EntryImpl* entry) { | 375 void Eviction::OnOpenEntryV2(EntryImpl* entry) { |
371 EntryStore* info = entry->entry()->Data(); | 376 EntryStore* info = entry->entry()->Data(); |
372 DCHECK_EQ(ENTRY_NORMAL, info->state); | 377 DCHECK_EQ(ENTRY_NORMAL, info->state); |
373 | 378 |
374 if (info->reuse_count < kint32max) { | 379 if (info->reuse_count < kint32max) { |
375 info->reuse_count++; | 380 info->reuse_count++; |
376 entry->entry()->set_modified(); | 381 entry->entry()->set_modified(); |
377 | 382 |
378 // We may need to move this to a new list. | 383 // We may need to move this to a new list. |
379 if (1 == info->reuse_count) { | 384 if (1 == info->reuse_count) { |
380 rankings_->Remove(entry->rankings(), Rankings::NO_USE); | 385 rankings_->Remove(entry->rankings(), Rankings::NO_USE, true); |
381 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); | 386 rankings_->Insert(entry->rankings(), false, Rankings::LOW_USE); |
382 entry->entry()->Store(); | 387 entry->entry()->Store(); |
383 } else if (kHighUse == info->reuse_count) { | 388 } else if (kHighUse == info->reuse_count) { |
384 rankings_->Remove(entry->rankings(), Rankings::LOW_USE); | 389 rankings_->Remove(entry->rankings(), Rankings::LOW_USE, true); |
385 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); | 390 rankings_->Insert(entry->rankings(), false, Rankings::HIGH_USE); |
386 entry->entry()->Store(); | 391 entry->entry()->Store(); |
387 } | 392 } |
388 } | 393 } |
389 } | 394 } |
390 | 395 |
391 void Eviction::OnCreateEntryV2(EntryImpl* entry) { | 396 void Eviction::OnCreateEntryV2(EntryImpl* entry) { |
392 EntryStore* info = entry->entry()->Data(); | 397 EntryStore* info = entry->entry()->Data(); |
393 switch (info->state) { | 398 switch (info->state) { |
394 case ENTRY_NORMAL: { | 399 case ENTRY_NORMAL: { |
395 DCHECK(!info->reuse_count); | 400 DCHECK(!info->reuse_count); |
396 DCHECK(!info->refetch_count); | 401 DCHECK(!info->refetch_count); |
397 break; | 402 break; |
398 }; | 403 }; |
399 case ENTRY_EVICTED: { | 404 case ENTRY_EVICTED: { |
400 if (info->refetch_count < kint32max) | 405 if (info->refetch_count < kint32max) |
401 info->refetch_count++; | 406 info->refetch_count++; |
402 | 407 |
403 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { | 408 if (info->refetch_count > kHighUse && info->reuse_count < kHighUse) { |
404 info->reuse_count = kHighUse; | 409 info->reuse_count = kHighUse; |
405 } else { | 410 } else { |
406 info->reuse_count++; | 411 info->reuse_count++; |
407 } | 412 } |
408 info->state = ENTRY_NORMAL; | 413 info->state = ENTRY_NORMAL; |
409 entry->entry()->Store(); | 414 entry->entry()->Store(); |
410 rankings_->Remove(entry->rankings(), Rankings::DELETED); | 415 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); |
411 break; | 416 break; |
412 }; | 417 }; |
413 default: | 418 default: |
414 NOTREACHED(); | 419 NOTREACHED(); |
415 } | 420 } |
416 | 421 |
417 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); | 422 rankings_->Insert(entry->rankings(), true, GetListForEntryV2(entry)); |
418 } | 423 } |
419 | 424 |
420 void Eviction::OnDoomEntryV2(EntryImpl* entry) { | 425 void Eviction::OnDoomEntryV2(EntryImpl* entry) { |
421 EntryStore* info = entry->entry()->Data(); | 426 EntryStore* info = entry->entry()->Data(); |
422 if (ENTRY_NORMAL != info->state) | 427 if (ENTRY_NORMAL != info->state) |
423 return; | 428 return; |
424 | 429 |
425 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry)); | 430 if (entry->LeaveRankingsBehind()) { |
| 431 info->state = ENTRY_DOOMED; |
| 432 entry->entry()->Store(); |
| 433 return; |
| 434 } |
| 435 |
| 436 rankings_->Remove(entry->rankings(), GetListForEntryV2(entry), true); |
426 | 437 |
427 info->state = ENTRY_DOOMED; | 438 info->state = ENTRY_DOOMED; |
428 entry->entry()->Store(); | 439 entry->entry()->Store(); |
429 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); | 440 rankings_->Insert(entry->rankings(), true, Rankings::DELETED); |
430 } | 441 } |
431 | 442 |
432 void Eviction::OnDestroyEntryV2(EntryImpl* entry) { | 443 void Eviction::OnDestroyEntryV2(EntryImpl* entry) { |
433 rankings_->Remove(entry->rankings(), Rankings::DELETED); | 444 if (entry->LeaveRankingsBehind()) |
| 445 return; |
| 446 |
| 447 rankings_->Remove(entry->rankings(), Rankings::DELETED, true); |
434 } | 448 } |
435 | 449 |
436 Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) { | 450 Rankings::List Eviction::GetListForEntryV2(EntryImpl* entry) { |
437 EntryStore* info = entry->entry()->Data(); | 451 EntryStore* info = entry->entry()->Data(); |
438 DCHECK_EQ(ENTRY_NORMAL, info->state); | 452 DCHECK_EQ(ENTRY_NORMAL, info->state); |
439 | 453 |
440 if (!info->reuse_count) | 454 if (!info->reuse_count) |
441 return Rankings::NO_USE; | 455 return Rankings::NO_USE; |
442 | 456 |
443 if (info->reuse_count < kHighUse) | 457 if (info->reuse_count < kHighUse) |
444 return Rankings::LOW_USE; | 458 return Rankings::LOW_USE; |
445 | 459 |
446 return Rankings::HIGH_USE; | 460 return Rankings::HIGH_USE; |
447 } | 461 } |
448 | 462 |
449 // This is a minimal implementation that just discards the oldest nodes. | 463 // This is a minimal implementation that just discards the oldest nodes. |
450 // TODO(rvargas): Do something better here. | 464 // TODO(rvargas): Do something better here. |
451 void Eviction::TrimDeleted(bool empty) { | 465 void Eviction::TrimDeleted(bool empty) { |
452 Trace("*** Trim Deleted ***"); | 466 Trace("*** Trim Deleted ***"); |
453 if (backend_->disabled_) | 467 if (backend_->disabled_) |
454 return; | 468 return; |
455 | 469 |
456 TimeTicks start = TimeTicks::Now(); | 470 TimeTicks start = TimeTicks::Now(); |
457 Rankings::ScopedRankingsBlock node(rankings_); | 471 Rankings::ScopedRankingsBlock node(rankings_); |
458 Rankings::ScopedRankingsBlock next(rankings_, | 472 Rankings::ScopedRankingsBlock next(rankings_, |
459 rankings_->GetPrev(node.get(), Rankings::DELETED)); | 473 rankings_->GetPrev(node.get(), Rankings::DELETED)); |
460 bool deleted = false; | 474 bool deleted = false; |
461 for (int i = 0; (i < 4 || empty) && next.get(); i++) { | 475 while (next.get() && |
| 476 (empty || (TimeTicks::Now() - start).InMilliseconds() < 20)) { |
462 node.reset(next.release()); | 477 node.reset(next.release()); |
463 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); | 478 next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED)); |
464 deleted |= RemoveDeletedNode(node.get()); | 479 deleted |= RemoveDeletedNode(node.get()); |
465 if (test_mode_) | 480 if (test_mode_) |
466 break; | 481 break; |
467 } | 482 } |
468 | 483 |
469 // Normally we use 25% for each list. The experiment doubles the number of | 484 // Normally we use 25% for each list. The experiment doubles the number of |
470 // deleted entries, so the total number of entries increases by 25%. Using | 485 // deleted entries, so the total number of entries increases by 25%. Using |
471 // 40% of that value for deleted entries leaves the size of the other three | 486 // 40% of that value for deleted entries leaves the size of the other three |
472 // lists intact. | 487 // lists intact. |
473 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : | 488 int max_length = in_experiment_ ? header_->num_entries * 2 / 5 : |
474 header_->num_entries / 4; | 489 header_->num_entries / 4; |
475 if (deleted && !empty && !test_mode_ && | 490 if (deleted && !empty && !test_mode_ && |
476 header_->lru.sizes[Rankings::DELETED] > max_length) { | 491 header_->lru.sizes[Rankings::DELETED] > max_length) { |
477 MessageLoop::current()->PostTask(FROM_HERE, | 492 MessageLoop::current()->PostTask(FROM_HERE, |
478 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); | 493 factory_.NewRunnableMethod(&Eviction::TrimDeleted, false)); |
479 } | 494 } |
480 | 495 |
481 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); | 496 CACHE_UMA(AGE_MS, "TotalTrimDeletedTime", 0, start); |
482 Trace("*** Trim Deleted end ***"); | 497 Trace("*** Trim Deleted end ***"); |
483 return; | 498 return; |
484 } | 499 } |
485 | 500 |
486 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { | 501 bool Eviction::RemoveDeletedNode(CacheRankingsBlock* node) { |
487 EntryImpl* entry = backend_->GetEnumeratedEntry(node); | 502 EntryImpl* entry = backend_->GetEnumeratedEntry(node, Rankings::DELETED); |
488 if (!entry) { | 503 if (!entry) { |
489 Trace("NewEntry failed on Trim 0x%x", node->address().value()); | 504 Trace("NewEntry failed on Trim 0x%x", node->address().value()); |
490 return false; | 505 return false; |
491 } | 506 } |
492 | 507 |
493 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); | 508 bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED); |
494 entry->entry()->Data()->state = ENTRY_DOOMED; | 509 entry->entry()->Data()->state = ENTRY_DOOMED; |
495 entry->DoomImpl(); | 510 entry->DoomImpl(); |
496 entry->Release(); | 511 entry->Release(); |
497 return !doomed; | 512 return !doomed; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
550 Time::FromInternalValue(last2.get()->Data()->last_used)); | 565 Time::FromInternalValue(last2.get()->Data()->last_used)); |
551 if (last3.get()) | 566 if (last3.get()) |
552 CACHE_UMA(AGE, "HighUseAge", 0, | 567 CACHE_UMA(AGE, "HighUseAge", 0, |
553 Time::FromInternalValue(last3.get()->Data()->last_used)); | 568 Time::FromInternalValue(last3.get()->Data()->last_used)); |
554 if (last4.get()) | 569 if (last4.get()) |
555 CACHE_UMA(AGE, "DeletedAge", 0, | 570 CACHE_UMA(AGE, "DeletedAge", 0, |
556 Time::FromInternalValue(last4.get()->Data()->last_used)); | 571 Time::FromInternalValue(last4.get()->Data()->last_used)); |
557 } | 572 } |
558 | 573 |
559 } // namespace disk_cache | 574 } // namespace disk_cache |
OLD | NEW |