Commit aa339eca authored by dskiba's avatar dskiba Committed by Commit bot

Defer allocation in cc::ContiguousContainerBase::Buffer.

This CL optimizes memory usage of cc::ContiguousContainer by deferring memory
allocation in ContiguousContainerBase::Buffer until the first Allocate() call.
Previously memory was allocated in the Buffer constructor, and sometimes was
never written to (see the bug for more info).

BUG=635142
CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_precise_blink_rel

Review-Url: https://codereview.chromium.org/2277433002
Cr-Commit-Position: refs/heads/master@{#414204}
parent 5a994226
......@@ -16,18 +16,22 @@ static const unsigned kDefaultInitialBufferSize = 32;
class ContiguousContainerBase::Buffer {
public:
explicit Buffer(size_t buffer_size)
: data_(new char[buffer_size]), end_(begin()), capacity_(buffer_size) {}
explicit Buffer(size_t buffer_size) : end_(nullptr), capacity_(buffer_size) {}
~Buffer() {}
size_t Capacity() const { return capacity_; }
size_t UsedCapacity() const { return end_ - begin(); }
size_t UnusedCapacity() const { return Capacity() - UsedCapacity(); }
size_t MemoryUsage() const { return begin() ? capacity_ : 0; }
bool empty() const { return UsedCapacity() == 0; }
void* Allocate(size_t object_size) {
DCHECK_GE(UnusedCapacity(), object_size);
if (!data_) {
data_.reset(new char[capacity_]);
end_ = begin();
}
void* result = end_;
end_ += object_size;
return result;
......@@ -40,8 +44,8 @@ class ContiguousContainerBase::Buffer {
}
private:
char* begin() { return &data_[0]; }
const char* begin() const { return &data_[0]; }
char* begin() { return data_.get(); }
const char* begin() const { return data_.get(); }
// begin() <= end_ <= begin() + capacity_
std::unique_ptr<char[]> data_;
......@@ -76,7 +80,10 @@ size_t ContiguousContainerBase::UsedCapacityInBytes() const {
}
size_t ContiguousContainerBase::MemoryUsageInBytes() const {
return sizeof(*this) + GetCapacityInBytes() +
size_t memory_usage = 0;
for (const auto& buffer : buffers_)
memory_usage += buffer->MemoryUsage();
return sizeof(*this) + memory_usage +
elements_.capacity() * sizeof(elements_[0]);
}
......
......@@ -4,6 +4,8 @@
#include <stddef.h>
#include <iterator>
#include "cc/base/contiguous_container.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -228,6 +230,15 @@ TEST(ContiguousContainerTest, ForwardIteration) {
"Non-const iteration should produce non-const references.");
}
TEST(ContiguousContainerTest, ForwardIterationEmpty) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
// ContiguousContainer allocates memory for elements lazily at first append
// operation, so at this point memory is not allocated. Check that iteration
// doesn't crash and produces zero elements.
EXPECT_EQ(0, std::distance(list.begin(), list.end()));
}
TEST(ContiguousContainerTest, ConstForwardIteration) {
ContiguousContainer<Point2D, kPointAlignment> list(kMaxPointSize);
for (int i = 0; i < (int)kNumElements; i++)
......@@ -511,6 +522,32 @@ TEST(ContiguousContainerTest, CapacityInBytesAfterClear) {
EXPECT_EQ(empty_capacity, list.GetCapacityInBytes());
}
TEST(ContiguousContainerTest, MemoryUsageInBytes) {
constexpr size_t initial_size1 = 10 * kMaxPointSize;
ContiguousContainer<Point2D, kPointAlignment> list1(kMaxPointSize,
initial_size1);
constexpr size_t initial_size2 = 10000 * kMaxPointSize;
ContiguousContainer<Point2D, kPointAlignment> list2(kMaxPointSize,
initial_size2);
// Memory is allocated lazily, so even though lists were created with
// different initial_size values, they'll have the same memory usage here.
size_t memory_usage1 = list1.MemoryUsageInBytes();
size_t memory_usage2 = list2.MemoryUsageInBytes();
EXPECT_EQ(memory_usage1, memory_usage2);
// Trigger memory allocation.
list1.AllocateAndConstruct<Point2D>();
list2.AllocateAndConstruct<Point2D>();
// Same object was created in both lists, but their memory usages grew
// differently, based on initial_size values lists were created with.
EXPECT_NE(list1.MemoryUsageInBytes(), list2.MemoryUsageInBytes());
EXPECT_GE(list1.MemoryUsageInBytes(), memory_usage1 + initial_size1);
EXPECT_GE(list2.MemoryUsageInBytes(), memory_usage2 + initial_size2);
}
TEST(ContiguousContainerTest, Alignment) {
const size_t max_align = ALIGNOF(long double);
ContiguousContainer<Point2D, max_align> list(kMaxPointSize);
......
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