Skip to content

Commit

Permalink
change backing store cache to be memory-based rather than count
Browse files Browse the repository at this point in the history
BUG=13763
TEST=none

Review URL: http://codereview.chromium.org/146095

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19246 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
erikkay@chromium.org committed Jun 25, 2009
1 parent a4ae3e1 commit c0040ac
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 44 deletions.
2 changes: 2 additions & 0 deletions base/gfx/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class Size {
int width() const { return width_; }
int height() const { return height_; }

int GetArea() const { return width_ * height_; }

void SetSize(int width, int height) {
set_width(width);
set_height(height);
Expand Down
4 changes: 4 additions & 0 deletions chrome/browser/renderer_host/backing_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class BackingStore {
RenderWidgetHost* render_widget_host() const { return render_widget_host_; }
const gfx::Size& size() { return size_; }

// The number of bytes that this backing store consumes. This should roughly
// be size_.GetArea() * bytes per pixel.
size_t MemorySize();

#if defined(OS_WIN)
HDC hdc() { return hdc_; }

Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/renderer_host/backing_store_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size)
BackingStore::~BackingStore() {
}

size_t BackingStore::MemorySize() {
// Always 4 bytes per pixel.
return size_.GetArea() * 4;
}

void BackingStore::PaintRect(base::ProcessHandle process,
TransportDIB* bitmap,
const gfx::Rect& bitmap_rect) {
Expand Down
161 changes: 117 additions & 44 deletions chrome/browser/renderer_host/backing_store_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,48 @@
#include "chrome/browser/renderer_host/render_widget_host_painting_observer.h"
#include "chrome/common/chrome_constants.h"


namespace {

// There are two separate caches, |large_cache| and |small_cache|. large_cache
// is meant for large items (tabs, popup windows), while small_cache is meant
// for small items (extension toolstrips and buttons, etc.). The idea is that
// we'll almost always try to evict from large_cache first since small_cache
// items will tend to be visible more of the time.
typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache;
static BackingStoreCache* cache = NULL;

// Returns the size of the backing store cache.
static size_t GetBackingStoreCacheSize() {
// This uses a similar approach to GetMaxRendererProcessCount. The goal
// is to reduce memory pressure and swapping on low-resource machines.
static const size_t kMaxDibCountByRamTier[] = {
2, // less than 256MB
3, // 256MB
4, // 512MB
5 // 768MB and above
};

static size_t max_size = kMaxDibCountByRamTier[
std::min(base::SysInfo::AmountOfPhysicalMemoryMB() / 256,
static_cast<int>(arraysize(kMaxDibCountByRamTier)) - 1)];
return max_size;
static BackingStoreCache* large_cache = NULL;
static BackingStoreCache* small_cache = NULL;

// Threshold is based on a large-monitor width toolstrip.
// TODO(erikkay) 32bpp assumption isn't great.
const size_t kSmallThreshold = 4 * 32 * 1920;

// Previously, the backing store cache was based on a set number of backing
// stores, regardless of their size. The numbers were chosen based on a user
// with a maximized browser on a large monitor. Now that the cache is based on
// total memory size of the backing stores, we'll keep an approximation of the
// numbers from the previous algorithm by choosing a large monitor backing store
// size as our multiplier.
// TODO(erikkay) Perhaps we should actually use monitor size? That way we
// could make an assertion like "worse case, there are two tabs in the cache".
// However, the small_cache might mess up these calculations a bit.
// TODO(erikkay) 32bpp assumption isn't great.
const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB

static size_t GetBackingStoreCacheMemorySize() {
// Compute in terms of the number of large monitor's worth of backing-store.
// Use a minimum of 2, and add one for each 256MB of physical memory you have.
// Cap at 5, the thinking being that even if you have a gigantic amount of
// RAM, there's a limit to how much caching helps beyond a certain number
// of tabs.
size_t mem_tier = std::min(5,
2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
return mem_tier * kMemoryMultiplier;
}

// Expires the given backing store from the cache.
void ExpireBackingStoreAt(BackingStoreCache::iterator backing_store) {
// Expires the given |backing_store| from |cache|.
void ExpireBackingStoreAt(BackingStoreCache* cache,
BackingStoreCache::iterator backing_store) {
RenderWidgetHost* rwh = backing_store->second->render_widget_host();
if (rwh->painting_observer()) {
rwh->painting_observer()->WidgetWillDestroyBackingStore(
Expand All @@ -43,32 +61,60 @@ void ExpireBackingStoreAt(BackingStoreCache::iterator backing_store) {
cache->Erase(backing_store);
}

void CreateCacheSpace(size_t size) {
// Given a request for |size|, first free from the large cache (until there's
// only one item left) and then do the same from the small cache if we still
// don't have enough.
while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
BackingStoreCache* cache =
(large_cache->size() > 1) ? large_cache : small_cache;
while (size > 0 && cache->size() > 1) {
// Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
// so we need to do -- to move one back to the actual last item.
BackingStoreCache::iterator entry = --cache->rbegin().base();
size_t entry_size = entry->second->MemorySize();
ExpireBackingStoreAt(cache, entry);
if (size > entry_size)
size -= entry_size;
else
size = 0;
}
}
DCHECK(size == 0);
}

// Creates the backing store for the host based on the dimensions passed in.
// Removes the existing backing store if there is one.
BackingStore* CreateBackingStore(RenderWidgetHost* host,
const gfx::Size& backing_store_size) {
// Remove any existing backing store in case we're replacing it.
BackingStoreManager::RemoveBackingStore(host);

size_t max_cache_size = GetBackingStoreCacheSize();
if (max_cache_size > 0 && !cache)
cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
if (!large_cache) {
large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
}

if (cache->size() >= max_cache_size) {
// Need to remove an old backing store to make room for the new one. We
// TODO(erikkay) 32bpp is not always accurate
size_t new_mem = backing_store_size.GetArea() * 4;
size_t current_mem = BackingStoreManager::MemorySize();
size_t max_mem = GetBackingStoreCacheMemorySize();
DCHECK(new_mem < max_mem);
if (current_mem + new_mem > max_mem) {
// Need to remove old backing stores to make room for the new one. We
// don't want to do this when the backing store is being replace by a new
// one for the same tab, but this case won't get called then: we'll have
// removed the onld one in the RemoveBackingStore above, and the cache
// won't be over-sized.
//
// Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
// so we need to do -- to move one back to the actual last item.
ExpireBackingStoreAt(--cache->rbegin().base());
CreateCacheSpace((current_mem + new_mem) - max_mem);
}
DCHECK((BackingStoreManager::MemorySize() + new_mem) < max_mem);

BackingStore* backing_store = host->AllocBackingStore(backing_store_size);
if (max_cache_size > 0)
cache->Put(host, backing_store);
if (new_mem > kSmallThreshold)
large_cache->Put(host, backing_store);
else
small_cache->Put(host, backing_store);
return backing_store;
}

Expand Down Expand Up @@ -119,35 +165,62 @@ BackingStore* BackingStoreManager::PrepareBackingStore(

// static
BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
if (cache) {
BackingStoreCache::iterator it = cache->Peek(host);
if (it != cache->end())
if (large_cache) {
BackingStoreCache::iterator it = large_cache->Get(host);
if (it != large_cache->end())
return it->second;

// This moves host to the front of the MRU.
it = small_cache->Get(host);
if (it != small_cache->end())
return it->second;
}
return NULL;
}

// static
void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
if (!cache)
if (!large_cache)
return;

BackingStoreCache* cache = large_cache;
BackingStoreCache::iterator it = cache->Peek(host);
if (it == cache->end())
return;
cache->Erase(it);

if (cache->empty()) {
delete cache;
cache = NULL;
if (it == cache->end()) {
cache = small_cache;
it = cache->Peek(host);
if (it == cache->end())
return;
}
cache->Erase(it);
}

// static
bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) {
BackingStoreCache* cache = large_cache;

BackingStoreCache::iterator it = cache->Peek(host);
if (it == cache->end())
return false;
ExpireBackingStoreAt(it);
if (it == cache->end()) {
cache = small_cache;
it = cache->Peek(host);
if (it == cache->end())
return false;
}
ExpireBackingStoreAt(cache, it);
return true;
}

// static
size_t BackingStoreManager::MemorySize() {
if (!large_cache)
return 0;

size_t mem = 0;
BackingStoreCache::iterator it;
for (it = large_cache->begin(); it != large_cache->end(); ++it)
mem += it->second->MemorySize();

for (it = small_cache->begin(); it != small_cache->end(); ++it)
mem += it->second->MemorySize();

return mem;
}
3 changes: 3 additions & 0 deletions chrome/browser/renderer_host/backing_store_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class BackingStoreManager {
// removed, false if it wasn't found.
static bool ExpireBackingStoreForTest(RenderWidgetHost* host);

// Current size in bytes of the backing store cache.
static size_t MemorySize();

private:
// Not intended for instantiation.
BackingStoreManager() {}
Expand Down
4 changes: 4 additions & 0 deletions chrome/browser/renderer_host/backing_store_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ BackingStore::~BackingStore() {
DeleteDC(hdc_);
}

size_t BackingStore::MemorySize() {
return size_.GetArea() * (color_depth_ / 8);
}

// static
bool BackingStore::ColorManagementEnabled() {
static bool enabled = false;
Expand Down
7 changes: 7 additions & 0 deletions chrome/browser/renderer_host/backing_store_x.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ BackingStore::~BackingStore() {
XFreeGC(display_, static_cast<GC>(pixmap_gc_));
}

size_t BackingStore::MemorySize() {
if (!use_render_)
return size_.GetArea() * (pixmap_bpp_ / 8);
else
return size_.GetArea() * 4;
}

void BackingStore::PaintRectWithoutXrender(TransportDIB* bitmap,
const gfx::Rect &bitmap_rect) {
const int width = bitmap_rect.width();
Expand Down

0 comments on commit c0040ac

Please sign in to comment.