Skip to content

Commit

Permalink
Estimate the potential performance impact for memory-backed code cache
Browse files Browse the repository at this point in the history
Add UMA histograms to measure the potential performance gain.

Change-Id: I3b035137c2b617fe5c944b3c5bf9b8b88458f126
Bug: 1324058
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3634940
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1002412}
  • Loading branch information
yutakahirano authored and Chromium LUCI CQ committed May 12, 2022
1 parent d9f9663 commit 3550a8f
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 9 deletions.
2 changes: 2 additions & 0 deletions content/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,8 @@ source_set("browser") {
"code_cache/generated_code_cache.h",
"code_cache/generated_code_cache_context.cc",
"code_cache/generated_code_cache_context.h",
"code_cache/simple_lru_cache_index.cc",
"code_cache/simple_lru_cache_index.h",
"compositor/surface_utils.cc",
"compositor/surface_utils.h",
"compute_pressure/compute_pressure_host.cc",
Expand Down
50 changes: 41 additions & 9 deletions content/browser/code_cache/generated_code_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
Expand Down Expand Up @@ -247,12 +248,14 @@ class GeneratedCodeCache::PendingOperation {
PendingOperation(Operation op,
const std::string& key,
const base::Time& response_time,
const base::TimeTicks start_time,
scoped_refptr<net::IOBufferWithSize> small_buffer,
scoped_refptr<BigIOBuffer> large_buffer,
ReadDataCallback read_callback)
: op_(op),
key_(key),
response_time_(response_time),
start_time_(start_time),
small_buffer_(small_buffer),
large_buffer_(large_buffer),
read_callback_(std::move(read_callback)) {
Expand All @@ -275,6 +278,28 @@ class GeneratedCodeCache::PendingOperation {
scoped_refptr<net::IOBufferWithSize> small_buffer() { return small_buffer_; }
scoped_refptr<BigIOBuffer> large_buffer() { return large_buffer_; }
ReadDataCallback TakeReadCallback() { return std::move(read_callback_); }
void RunReadCallback(GeneratedCodeCache* code_cache,
base::Time response_time,
mojo_base::BigBuffer data) {
if (code_cache->cache_type_ == CodeCacheType::kJavaScript) {
const bool code_cache_hit = data.size() > 0;
const bool hypothetical_in_memory_code_cache_hit =
code_cache->lru_cache_index_.Get(key_);
if (code_cache_hit && !hypothetical_in_memory_code_cache_hit) {
code_cache->lru_cache_index_.Put(key_, data.size());
}
if (code_cache_hit && hypothetical_in_memory_code_cache_hit) {
base::UmaHistogramTimes(
"SiteIsolatedCodeCache.JS.MemoryBackedCodeCachePotentialImpact",
base::TimeTicks::Now() - start_time_);
}
base::UmaHistogramBoolean("SiteIsolatedCodeCache.JS.Hit", code_cache_hit);
base::UmaHistogramBoolean(
"SiteIsolatedCodeCache.JS.PotentialMemoryBackedCodeCacheHit",
hypothetical_in_memory_code_cache_hit);
}
std::move(read_callback_).Run(response_time, std::move(data));
}
GetBackendCallback TakeBackendCallback() {
return std::move(backend_callback_);
}
Expand All @@ -296,6 +321,8 @@ class GeneratedCodeCache::PendingOperation {
return response_time_;
}

base::TimeTicks start_time() const { return start_time_; }

// These are called by write and fetch operations to track buffer completions
// and signal when the operation has finished, and whether it was successful.
bool succeeded() const { return succeeded_; }
Expand All @@ -314,6 +341,7 @@ class GeneratedCodeCache::PendingOperation {
const Operation op_;
const std::string key_;
const base::Time response_time_;
const base::TimeTicks start_time_ = base::TimeTicks::Now();
scoped_refptr<net::IOBufferWithSize> small_buffer_;
scoped_refptr<BigIOBuffer> large_buffer_;
ReadDataCallback read_callback_;
Expand Down Expand Up @@ -432,6 +460,7 @@ void GeneratedCodeCache::WriteEntry(const GURL& url,
auto op = std::make_unique<PendingOperation>(Operation::kWrite, key,
small_buffer, large_buffer);
EnqueueOperation(std::move(op));
lru_cache_index_.Put(key, data_size);
}

void GeneratedCodeCache::FetchEntry(const GURL& url,
Expand Down Expand Up @@ -463,6 +492,8 @@ void GeneratedCodeCache::DeleteEntry(const GURL& url,
std::string key = GetCacheKey(url, origin_lock, nik, cache_type_);
auto op = std::make_unique<PendingOperation>(Operation::kDelete, key);
EnqueueOperation(std::move(op));

lru_cache_index_.Delete(key);
}

void GeneratedCodeCache::CreateBackend() {
Expand Down Expand Up @@ -660,7 +691,7 @@ void GeneratedCodeCache::FetchEntryImpl(PendingOperation* op) {
DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (backend_state_ != kInitialized) {
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
op->RunReadCallback(this, base::Time(), mojo_base::BigBuffer());
CloseOperationAndIssueNext(op);
return;
}
Expand All @@ -682,7 +713,7 @@ void GeneratedCodeCache::OpenCompleteForRead(
Operation::kFetchWithSHAKey == op->operation());
if (entry_result.net_error() != net::OK) {
CollectStatistics(CacheEntryStatus::kMiss);
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
op->RunReadCallback(this, base::Time(), mojo_base::BigBuffer());
CloseOperationAndIssueNext(op);
return;
}
Expand Down Expand Up @@ -759,7 +790,7 @@ void GeneratedCodeCache::ReadComplete(PendingOperation* op) {
DCHECK(Operation::kFetch == op->operation() ||
Operation::kFetchWithSHAKey == op->operation());
if (!op->succeeded()) {
op->TakeReadCallback().Run(base::Time(), mojo_base::BigBuffer());
op->RunReadCallback(this, base::Time(), mojo_base::BigBuffer());
// Doom this entry since it is inaccessible.
DoomEntry(op);
} else {
Expand All @@ -773,12 +804,12 @@ void GeneratedCodeCache::ReadComplete(PendingOperation* op) {
mojo_base::BigBuffer data(data_size);
memcpy(data.data(), op->small_buffer()->data() + kHeaderSizeInBytes,
data_size);
op->TakeReadCallback().Run(response_time, std::move(data));
op->RunReadCallback(this, response_time, std::move(data));
} else if (!ShouldDeduplicateEntry(data_size)) {
// Large data below the merging threshold, or deduplication is disabled.
// Return the large buffer.
op->TakeReadCallback().Run(response_time,
op->large_buffer()->TakeBuffer());
op->RunReadCallback(this, response_time,
op->large_buffer()->TakeBuffer());
} else {
// Very large data. Create the second fetch using the checksum as key.
DCHECK_EQ(static_cast<int>(kHeaderSizeInBytes + kSHAKeySizeInBytes),
Expand All @@ -790,13 +821,14 @@ void GeneratedCodeCache::ReadComplete(PendingOperation* op) {
auto large_buffer = base::MakeRefCounted<BigIOBuffer>(data_size);
auto op2 = std::make_unique<PendingOperation>(
Operation::kFetchWithSHAKey, checksum_key, response_time,
small_buffer, large_buffer, op->TakeReadCallback());
op->start_time(), small_buffer, large_buffer,
op->TakeReadCallback());
EnqueueOperation(std::move(op2));
}
} else {
// Large merged code data with no header. |op| holds the response time.
op->TakeReadCallback().Run(op->response_time(),
op->large_buffer()->TakeBuffer());
op->RunReadCallback(this, op->response_time(),
op->large_buffer()->TakeBuffer());
}
}
CloseOperationAndIssueNext(op);
Expand Down
4 changes: 4 additions & 0 deletions content/browser/code_cache/generated_code_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "base/containers/queue.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "content/browser/code_cache/simple_lru_cache_index.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "net/base/io_buffer.h"
Expand Down Expand Up @@ -233,6 +234,9 @@ class CONTENT_EXPORT GeneratedCodeCache {
int max_size_bytes_;
CodeCacheType cache_type_;

// A hypothetical memory-backed code cache. Used to collect UMAs.
SimpleLruCacheIndex lru_cache_index_{/*capacity=*/200 * 1024 * 1024};

base::WeakPtrFactory<GeneratedCodeCache> weak_ptr_factory_{this};
};

Expand Down
70 changes: 70 additions & 0 deletions content/browser/code_cache/simple_lru_cache_index.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/code_cache/simple_lru_cache_index.h"

#include <limits>

#include "net/base/url_util.h"

namespace content {

SimpleLruCacheIndex::SimpleLruCacheIndex(uint64_t capacity)
: capacity_(capacity) {}
SimpleLruCacheIndex::~SimpleLruCacheIndex() = default;

bool SimpleLruCacheIndex::Get(const std::string& key) {
const auto it = entries_.find(key);
if (it == entries_.end()) {
return false;
}
const Age age = GetNextAge();
access_list_.erase(it->second.age);
it->second.age = age;
access_list_.emplace(age, it->first);
return true;
}

void SimpleLruCacheIndex::Put(const std::string& key, uint32_t payload_size) {
Delete(key);

const Age age = GetNextAge();
const uint32_t size =
std::min(payload_size,
std::numeric_limits<uint32_t>::max() - kEmptyEntrySize) +
kEmptyEntrySize;

entries_.emplace(key, Value(age, size));
access_list_.emplace(age, std::move(key));
size_ += size;
Evict();
}

void SimpleLruCacheIndex::Delete(const std::string& key) {
const auto it = entries_.find(key);
if (it == entries_.end()) {
return;
}

DCHECK_GE(size_, it->second.size);
size_ -= it->second.size;
access_list_.erase(it->second.age);
entries_.erase(it);
}

uint64_t SimpleLruCacheIndex::GetSize() const {
return size_;
}

void SimpleLruCacheIndex::Evict() {
while (capacity_ < size_) {
auto it = access_list_.begin();
DCHECK(it != access_list_.end());
DCHECK(entries_.find(it->second) != entries_.end());

Delete(it->second);
}
}

} // namespace content
62 changes: 62 additions & 0 deletions content/browser/code_cache/simple_lru_cache_index.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_
#define CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_

#include <stdint.h>

#include <map>
#include <string>

#include "base/types/strong_alias.h"
#include "content/common/content_export.h"

namespace content {

// A simple LRU cache index, to measure the potential performance impact of
// memory-backed code cache.
class CONTENT_EXPORT SimpleLruCacheIndex {
public:
explicit SimpleLruCacheIndex(uint64_t capacity);
~SimpleLruCacheIndex();

SimpleLruCacheIndex(const SimpleLruCacheIndex&) = delete;
SimpleLruCacheIndex& operator=(const SimpleLruCacheIndex&) = delete;

// Returns whether an entry for `key` exists in the cache.
bool Get(const std::string& key);
// Puts an entry whose payload size is `size` to the cache.
void Put(const std::string& key, uint32_t size);
// Deletes an entry for `key` in the cache. If there is no such an entry, this
// does nothing.
void Delete(const std::string& key);
// Returns the total size of the cache.
uint64_t GetSize() const;

static constexpr uint32_t kEmptyEntrySize = 2048;

private:
using Age = base::StrongAlias<class AgeTag, uint32_t>;
using Key = std::string;
struct Value {
Value(Age age, uint32_t size) : age(age), size(size) {}

Age age;
uint32_t size;
};

Age GetNextAge() { return Age(age_source_++); }
void Evict();

const uint64_t capacity_;
std::map<Key, Value> entries_;
std::map<Age, Key> access_list_;
uint32_t age_source_ = 0;
uint64_t size_ = 0;
};

} // namespace content

#endif // CONTENT_BROWSER_CODE_CACHE_SIMPLE_LRU_CACHE_INDEX_H_

0 comments on commit 3550a8f

Please sign in to comment.