Commit 65f2cba2 authored by Maks Orlovich's avatar Maks Orlovich Committed by Commit Bot

disk_cache::MemBackend --- fix GetAvailableRange over big, sparse ranges

Previous code both would perform very badly --- as it's scanning over 4K
increments, which is iffy if there is a big hole in the middle --- and
could also suffer from wrap around in index computations (an alternative
may be to fix just that...)

This change replace hash table keeping track of ranges with the ordered
std::map, which makes finding adjacent kids easy.

Bug: 808561
Change-Id: I3442bbbf6c0e4bc6089ba89265ebb27d739d7edc
Reviewed-on: https://chromium-review.googlesource.com/901862
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534731}
parent 053af6f7
...@@ -1821,6 +1821,12 @@ void DiskCacheEntryTest::GetAvailableRange() { ...@@ -1821,6 +1821,12 @@ void DiskCacheEntryTest::GetAvailableRange() {
EXPECT_EQ(0, cb.GetResult(rv)); EXPECT_EQ(0, cb.GetResult(rv));
EXPECT_EQ(kTinyLen * 2, start); EXPECT_EQ(kTinyLen * 2, start);
// Get a huge range with maximum boundary
start = -1;
rv = entry->GetAvailableRange(0x2100000, std::numeric_limits<int32_t>::max(),
&start, cb.callback());
EXPECT_EQ(0, cb.GetResult(rv));
entry->Close(); entry->Close();
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/values.h" #include "base/values.h"
#include "net/base/interval.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
#include "net/disk_cache/memory/mem_backend_impl.h" #include "net/disk_cache/memory/mem_backend_impl.h"
...@@ -527,36 +528,35 @@ int MemEntryImpl::InternalGetAvailableRange(int64_t offset, ...@@ -527,36 +528,35 @@ int MemEntryImpl::InternalGetAvailableRange(int64_t offset,
if (offset < 0 || len < 0 || !start) if (offset < 0 || len < 0 || !start)
return net::ERR_INVALID_ARGUMENT; return net::ERR_INVALID_ARGUMENT;
MemEntryImpl* current_child = nullptr; net::Interval<int64_t> requested(offset, offset + len);
// Find the first child and record the number of empty bytes. // Find the first relevant child, if any --- may have to skip over
int empty = FindNextChild(offset, len, &current_child); // one entry as it may be before the range (consider, for example,
if (current_child && empty < len) { // if the request is for [2048, 10000), while [0, 1024) is a valid range
*start = offset + empty; // for the entry).
len -= empty; EntryMap::const_iterator i = children_->lower_bound(ToChildIndex(offset));
if (i != children_->cend() && !ChildInterval(i).Intersects(requested))
// Counts the number of continuous bytes. ++i;
int continuous = 0; net::Interval<int64_t> found;
if (i != children_->cend() &&
// This loop scan for continuous bytes. requested.Intersects(ChildInterval(i), &found)) {
while (len && current_child) { // Found something relevant; now just need to expand this out if next
// Number of bytes available in this child. // children are contiguous and relevant to the request.
int data_size = current_child->GetDataSize(kSparseData) - while (true) {
ToChildOffset(*start + continuous); ++i;
if (data_size > len) net::Interval<int64_t> relevant_in_next_child;
data_size = len; if (i == children_->cend() ||
!requested.Intersects(ChildInterval(i), &relevant_in_next_child) ||
// We have found more continuous bytes so increment the count. Also relevant_in_next_child.min() != found.max()) {
// decrement the length we should scan.
continuous += data_size;
len -= data_size;
// If the next child is discontinuous, break the loop.
if (FindNextChild(*start + continuous, len, &current_child))
break; break;
}
found.SpanningUnion(relevant_in_next_child);
} }
return continuous; *start = found.min();
return found.Length();
} }
*start = offset; *start = offset;
return 0; return 0;
} }
...@@ -589,36 +589,17 @@ MemEntryImpl* MemEntryImpl::GetChild(int64_t offset, bool create) { ...@@ -589,36 +589,17 @@ MemEntryImpl* MemEntryImpl::GetChild(int64_t offset, bool create) {
return nullptr; return nullptr;
} }
int MemEntryImpl::FindNextChild(int64_t offset, int len, MemEntryImpl** child) { net::Interval<int64_t> MemEntryImpl::ChildInterval(
DCHECK(child); MemEntryImpl::EntryMap::const_iterator i) {
*child = nullptr; DCHECK(i != children_->cend());
int scanned_len = 0; const MemEntryImpl* child = i->second;
// The valid range in child is [child_first_pos_, DataSize), since the child
// This loop tries to find the first existing child. // entry ops just use standard disk_cache::Entry API, so DataSize is
while (scanned_len < len) { // not aware of any hole in the beginning.
// This points to the current offset in the child. int64_t child_responsibility_start = (i->first) * kMaxSparseEntrySize;
int current_child_offset = ToChildOffset(offset + scanned_len); return net::Interval<int64_t>(
MemEntryImpl* current_child = GetChild(offset + scanned_len, false); child_responsibility_start + child->child_first_pos_,
if (current_child) { child_responsibility_start + child->GetDataSize(kSparseData));
int child_first_pos = current_child->child_first_pos_;
// This points to the first byte that we should be reading from, we need
// to take care of the filled region and the current offset in the child.
int first_pos = std::max(current_child_offset, child_first_pos);
// If the first byte position we should read from doesn't exceed the
// filled region, we have found the first child.
if (first_pos < current_child->GetDataSize(kSparseData)) {
*child = current_child;
// We need to advance the scanned length.
scanned_len += first_pos - current_child_offset;
break;
}
}
scanned_len += kMaxSparseEntrySize - current_child_offset;
}
return scanned_len;
} }
} // namespace disk_cache } // namespace disk_cache
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
#include <stdint.h> #include <stdint.h>
#include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
#include "base/containers/linked_list.h" #include "base/containers/linked_list.h"
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/trace_event/memory_usage_estimator.h" #include "base/trace_event/memory_usage_estimator.h"
#include "net/base/interval.h"
#include "net/base/net_export.h" #include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h" #include "net/disk_cache/disk_cache.h"
#include "net/log/net_log_with_source.h" #include "net/log/net_log_with_source.h"
...@@ -140,7 +141,7 @@ class NET_EXPORT_PRIVATE MemEntryImpl final ...@@ -140,7 +141,7 @@ class NET_EXPORT_PRIVATE MemEntryImpl final
MemEntryImpl* parent, MemEntryImpl* parent,
net::NetLog* net_log); net::NetLog* net_log);
using EntryMap = std::unordered_map<int, MemEntryImpl*>; using EntryMap = std::map<int, MemEntryImpl*>;
static const int kNumStreams = 3; static const int kNumStreams = 3;
...@@ -165,10 +166,11 @@ class NET_EXPORT_PRIVATE MemEntryImpl final ...@@ -165,10 +166,11 @@ class NET_EXPORT_PRIVATE MemEntryImpl final
// created. // created.
MemEntryImpl* GetChild(int64_t offset, bool create); MemEntryImpl* GetChild(int64_t offset, bool create);
// Finds the first child located within the range [|offset|, |offset + len|). // Returns an interval describing what's stored in the child entry pointed to
// Returns the number of bytes ahead of |offset| to reach the first available // by i, in global coordinates.
// bytes in the entry. The first child found is output to |child|. // Precondition: i != children_.end();
int FindNextChild(int64_t offset, int len, MemEntryImpl** child); net::Interval<int64_t> ChildInterval(
MemEntryImpl::EntryMap::const_iterator i);
std::string key_; std::string key_;
std::vector<char> data_[kNumStreams]; // User data. std::vector<char> data_[kNumStreams]; // User data.
......
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