Commit 6ba02e29 authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

[code caching] Eliminate a copy in GeneratedCodeCache

- Adds a custom BigIOBuffer subclass of net::IOBufferWithSize,
  backed by a mojo_base::BigBuffer.
- Uses this buffer to avoid a copy for large code data (> 64KB). In
  order to avoid a copy, the disk entry is read in two parts, response
  time and code.
- Small code data (< 64 KB) uses a single read and copies as before.

Bug: chromium:992991
Change-Id: I520c8983b04c323e8ea9c8d817f94a3a64b4f7c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1769084Reviewed-by: default avatarKen Rockot <rockot@google.com>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Bill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691658}
parent 81e294be
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
namespace content { namespace content {
namespace { namespace {
constexpr char kPrefix[] = "_key"; constexpr char kPrefix[] = "_key";
constexpr char kSeparator[] = " \n"; constexpr char kSeparator[] = " \n";
...@@ -66,6 +67,45 @@ std::string GetCacheKey(const GURL& resource_url, const GURL& origin_lock) { ...@@ -66,6 +67,45 @@ std::string GetCacheKey(const GURL& resource_url, const GURL& origin_lock) {
return key; return key;
} }
constexpr int kResponseTimeSizeInBytes = sizeof(int64_t);
static_assert(mojo_base::BigBuffer::kMaxInlineBytes >=
2 * kResponseTimeSizeInBytes,
"Buffer may not be large enough for response time");
static_assert(mojo_base::BigBuffer::kMaxInlineBytes <=
std::numeric_limits<int>::max(),
"Buffer size calculations may overflow int");
// The cutoff size for small code entries. Anything larger than this uses a
// shared memory BigBuffer to read the entry.
constexpr int kSmallCodeMaximumInBytes = mojo_base::BigBuffer::kMaxInlineBytes;
// A net::IOBufferWithSize backed by a mojo_base::BigBuffer. For very large
// code, using BigBuffer allows us to avoid slow data copies.
class BigIOBuffer : public net::IOBufferWithSize {
public:
explicit BigIOBuffer(size_t size) : net::IOBufferWithSize(nullptr, size) {
DCHECK_GT(size, static_cast<size_t>(kSmallCodeMaximumInBytes));
buffer_ = mojo_base::BigBuffer(size);
data_ = reinterpret_cast<char*>(buffer_.data());
DCHECK(data_);
}
mojo_base::BigBuffer TakeBuffer() { return std::move(buffer_); }
protected:
~BigIOBuffer() override {
// Storage is managed by BigBuffer. We must clear these before the base
// class destructor runs.
this->data_ = nullptr;
this->size_ = 0UL;
}
private:
mojo_base::BigBuffer buffer_;
DISALLOW_COPY_AND_ASSIGN(BigIOBuffer);
};
} // namespace } // namespace
std::string GeneratedCodeCache::GetResourceURLFromKey(const std::string& key) { std::string GeneratedCodeCache::GetResourceURLFromKey(const std::string& key) {
...@@ -455,22 +495,42 @@ void GeneratedCodeCache::OpenCompleteForReadData( ...@@ -455,22 +495,42 @@ void GeneratedCodeCache::OpenCompleteForReadData(
return; return;
} }
int result = net::ERR_FAILED; disk_cache::ScopedEntryPtr disk_entry(entry_result.ReleaseEntry());
scoped_refptr<net::IOBufferWithSize> buffer; // There should be a valid entry if the open was successful.
{ DCHECK(disk_entry);
disk_cache::ScopedEntryPtr disk_entry(entry_result.ReleaseEntry());
// There should be a valid entry if the open was successful. int entry_size = disk_entry->GetDataSize(kDataIndex);
DCHECK(disk_entry); if (entry_size <= kSmallCodeMaximumInBytes) {
int size = disk_entry->GetDataSize(kDataIndex); // This entry is small. Read response time and code together and separate
buffer = base::MakeRefCounted<net::IOBufferWithSize>(size); // them before we copy the buffer for transfer.
scoped_refptr<net::IOBufferWithSize> buffer =
base::MakeRefCounted<net::IOBufferWithSize>(entry_size);
net::CompletionOnceCallback callback = base::BindOnce( net::CompletionOnceCallback callback = base::BindOnce(
&GeneratedCodeCache::ReadDataComplete, weak_ptr_factory_.GetWeakPtr(), &GeneratedCodeCache::ReadDataComplete, weak_ptr_factory_.GetWeakPtr(),
key, read_data_callback, buffer); key, read_data_callback, buffer);
result = disk_entry->ReadData(kDataIndex, 0, buffer.get(), size, int result = disk_entry->ReadData(kDataIndex, 0, buffer.get(), entry_size,
std::move(callback)); std::move(callback));
} if (result != net::ERR_IO_PENDING) {
if (result != net::ERR_IO_PENDING) { ReadDataComplete(key, read_data_callback, buffer, result);
ReadDataComplete(key, read_data_callback, buffer, result); }
} else {
// This entry is large. Use a BigIOBuffer backed by shared memory to read
// and transfer it without copying. We have to read the data in two parts,
// response time and code, since we can't trim the shared memory. Use the
// same buffer to read the response time and the code.
int code_size = entry_size - kResponseTimeSizeInBytes;
// Release the disk entry to pass it to |ReadResponseTimeComplete|.
disk_cache::Entry* entry = disk_entry.release();
scoped_refptr<net::IOBufferWithSize> buffer =
base::MakeRefCounted<BigIOBuffer>(static_cast<size_t>(code_size));
net::CompletionOnceCallback callback = base::BindOnce(
&GeneratedCodeCache::ReadResponseTimeComplete,
weak_ptr_factory_.GetWeakPtr(), key, read_data_callback, buffer, entry);
int result = entry->ReadData(kDataIndex, 0, buffer.get(),
kResponseTimeSizeInBytes, std::move(callback));
if (result != net::ERR_IO_PENDING) {
ReadResponseTimeComplete(key, read_data_callback, buffer, entry, result);
}
} }
} }
...@@ -504,6 +564,52 @@ void GeneratedCodeCache::ReadDataComplete( ...@@ -504,6 +564,52 @@ void GeneratedCodeCache::ReadDataComplete(
IssueQueuedOperationForEntry(key); IssueQueuedOperationForEntry(key);
} }
void GeneratedCodeCache::ReadResponseTimeComplete(
const std::string& key,
ReadDataCallback read_data_callback,
scoped_refptr<net::IOBufferWithSize> buffer,
disk_cache::Entry* entry,
int rv) {
DCHECK(entry);
disk_cache::ScopedEntryPtr disk_entry(entry);
if (rv != kResponseTimeSizeInBytes) {
CollectStatistics(CacheEntryStatus::kMiss);
std::move(read_data_callback).Run(base::Time(), mojo_base::BigBuffer());
} else {
// This is considered a cache hit, since response time was read.
CollectStatistics(CacheEntryStatus::kHit);
int64_t raw_response_time = *(reinterpret_cast<int64_t*>(buffer->data()));
net::CompletionOnceCallback callback = base::BindOnce(
&GeneratedCodeCache::ReadCodeComplete, weak_ptr_factory_.GetWeakPtr(),
key, read_data_callback, buffer, raw_response_time);
int result =
disk_entry->ReadData(kDataIndex, kResponseTimeSizeInBytes, buffer.get(),
buffer->size(), std::move(callback));
if (result != net::ERR_IO_PENDING) {
ReadCodeComplete(key, read_data_callback, buffer, raw_response_time,
result);
}
}
}
void GeneratedCodeCache::ReadCodeComplete(
const std::string& key,
ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer,
int64_t raw_response_time,
int rv) {
if (rv != buffer->size()) {
// If the buffer is invalid, return an empty one with no response time.
std::move(callback).Run(base::Time(), mojo_base::BigBuffer());
} else {
base::Time response_time = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(raw_response_time));
std::move(callback).Run(
response_time, static_cast<BigIOBuffer*>(buffer.get())->TakeBuffer());
}
IssueQueuedOperationForEntry(key);
}
void GeneratedCodeCache::DeleteEntryImpl(const std::string& key) { void GeneratedCodeCache::DeleteEntryImpl(const std::string& key) {
if (backend_state_ != kInitialized) if (backend_state_ != kInitialized)
return; return;
......
...@@ -47,7 +47,6 @@ class CONTENT_EXPORT GeneratedCodeCache { ...@@ -47,7 +47,6 @@ class CONTENT_EXPORT GeneratedCodeCache {
base::RepeatingCallback<void(const base::Time&, base::RepeatingCallback<void(const base::Time&,
mojo_base::BigBuffer data)>; mojo_base::BigBuffer data)>;
using GetBackendCallback = base::OnceCallback<void(disk_cache::Backend*)>; using GetBackendCallback = base::OnceCallback<void(disk_cache::Backend*)>;
static const int kResponseTimeSizeInBytes = sizeof(int64_t);
// Cache type. Used for collecting statistics for JS and Wasm in separate // Cache type. Used for collecting statistics for JS and Wasm in separate
// buckets. // buckets.
...@@ -153,6 +152,16 @@ class CONTENT_EXPORT GeneratedCodeCache { ...@@ -153,6 +152,16 @@ class CONTENT_EXPORT GeneratedCodeCache {
ReadDataCallback callback, ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer, scoped_refptr<net::IOBufferWithSize> buffer,
int rv); int rv);
void ReadResponseTimeComplete(const std::string& key,
ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer,
disk_cache::Entry* entry,
int rv);
void ReadCodeComplete(const std::string& key,
ReadDataCallback callback,
scoped_refptr<net::IOBufferWithSize> buffer,
int64_t raw_response_time,
int rv);
// Delete entry from cache // Delete entry from cache
void DeleteEntryImpl(const std::string& key); void DeleteEntryImpl(const std::string& key);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment