forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Courgette] Initial Implementation of LabelManager
This is part of the effort to reduce Courgette's peak memory. Main changes: - Moving Label to image_utils.h, and change Label::count_ from int to int32. - Adding utility class ConsecutiveRangeVisitor, with tests. - Adding LabelManager, with tests. The new code is not yet used in production. Review URL: https://codereview.chromium.org/1491703003 Cr-Commit-Position: refs/heads/master@{#363688}
- Loading branch information
1 parent
56c43a0
commit 7a2fea2
Showing
9 changed files
with
439 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2015 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 COURGETTE_CONSECUTIVE_RANGE_VISITOR_H_ | ||
#define COURGETTE_CONSECUTIVE_RANGE_VISITOR_H_ | ||
|
||
#include <iterator> | ||
|
||
#include "base/macros.h" | ||
|
||
namespace courgette { | ||
|
||
// Usage note: First check whether std::unique() would suffice. | ||
// | ||
// ConsecutiveRangeVisitor is a visitor to read equal consecutive items | ||
// ("ranges") between two iterators. The base value of InputIterator must | ||
// implement the == operator. | ||
// | ||
// Example: "AAAAABZZZZOO" consists of ranges ["AAAAA", "B", "ZZZZ", "OO"]. The | ||
// visitor provides accessors to iterate through the ranges, and to access each | ||
// range's value and repeat, i.e., [('A', 5), ('B', 1), ('Z', 4), ('O', 2)]. | ||
template <class InputIterator> | ||
class ConsecutiveRangeVisitor { | ||
public: | ||
ConsecutiveRangeVisitor(InputIterator begin, InputIterator end) | ||
: head_(begin), end_(end) { | ||
advance(); | ||
} | ||
|
||
// Returns whether there are more ranges to traverse. | ||
bool has_more() const { return tail_ != end_; } | ||
|
||
// Returns an iterator to an element in the current range. | ||
InputIterator cur() const { return tail_; } | ||
|
||
// Returns the number of repeated elements in the current range. | ||
size_t repeat() const { return std::distance(tail_, head_); } | ||
|
||
// Advances to the next range. | ||
void advance() { | ||
tail_ = head_; | ||
if (head_ != end_) | ||
while (++head_ != end_ && *head_ == *tail_) {} | ||
} | ||
|
||
private: | ||
InputIterator tail_; // The trailing pionter of a range (inclusive). | ||
InputIterator head_; // The leading pointer of a range (exclusive). | ||
InputIterator end_; // Store the end pointer so we know when to stop. | ||
|
||
DISALLOW_COPY_AND_ASSIGN(ConsecutiveRangeVisitor); | ||
}; | ||
|
||
} // namespace courgette | ||
|
||
#endif // COURGETTE_CONSECUTIVE_RANGE_VISITOR_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2015 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 "courgette/consecutive_range_visitor.h" | ||
|
||
#include <string> | ||
|
||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace courgette { | ||
|
||
TEST(ConsecutiveRangeVisitorTest, Basic) { | ||
std::string s = "AAAAABZZZZOO"; | ||
ConsecutiveRangeVisitor<std::string::iterator> vis(s.begin(), s.end()); | ||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ('A', *vis.cur()); | ||
EXPECT_EQ(5U, vis.repeat()); | ||
vis.advance(); | ||
|
||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ('B', *vis.cur()); | ||
EXPECT_EQ(1U, vis.repeat()); | ||
vis.advance(); | ||
|
||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ('Z', *vis.cur()); | ||
EXPECT_EQ(4U, vis.repeat()); | ||
vis.advance(); | ||
|
||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ('O', *vis.cur()); | ||
EXPECT_EQ(2U, vis.repeat()); | ||
vis.advance(); | ||
|
||
EXPECT_FALSE(vis.has_more()); | ||
} | ||
|
||
TEST(ConsecutiveRangeVisitorTest, UnitRanges) { | ||
// Unsorted, no consecutive characters. | ||
const char s[] = "elephant elephant"; | ||
ConsecutiveRangeVisitor<const char*> vis(std::begin(s), std::end(s) - 1); | ||
for (const char* scan = &s[0]; *scan; ++scan) { | ||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ(*scan, *vis.cur()); | ||
EXPECT_EQ(1U, vis.repeat()); | ||
vis.advance(); | ||
} | ||
EXPECT_FALSE(vis.has_more()); | ||
} | ||
|
||
TEST(ConsecutiveRangeVisitorTest, SingleRange) { | ||
for (size_t len = 1U; len < 10U; ++len) { | ||
std::vector<int> v(len, 137); | ||
ConsecutiveRangeVisitor<std::vector<int>::iterator> vis(v.begin(), v.end()); | ||
EXPECT_TRUE(vis.has_more()); | ||
EXPECT_EQ(137, *vis.cur()); | ||
EXPECT_EQ(len, vis.repeat()); | ||
vis.advance(); | ||
EXPECT_FALSE(vis.has_more()); | ||
} | ||
} | ||
|
||
TEST(ConsecutiveRangeVisitorTest, Empty) { | ||
std::string s; | ||
ConsecutiveRangeVisitor<std::string::iterator> vis(s.begin(), s.end()); | ||
EXPECT_FALSE(vis.has_more()); | ||
} | ||
|
||
} // namespace courgette |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2015 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 "courgette/label_manager.h" | ||
|
||
#include <algorithm> | ||
|
||
#include "base/logging.h" | ||
#include "base/numerics/safe_math.h" | ||
#include "courgette/consecutive_range_visitor.h" | ||
|
||
namespace courgette { | ||
|
||
LabelManager::RvaVisitor::~RvaVisitor() {} | ||
|
||
LabelManager::LabelManager() {} | ||
|
||
LabelManager::~LabelManager() {} | ||
|
||
// We wish to minimize peak memory usage here. Analysis: Let | ||
// m = number of (RVA) elements in |rva_visitor|, | ||
// n = number of distinct (RVA) elements in |rva_visitor|. | ||
// The final storage is n * sizeof(Label) bytes. During computation we uniquify | ||
// m RVAs, and count repeats. Taking sizeof(RVA) = 4, an implementation using | ||
// std::map or std::unordered_map would consume additionally 32 * n bytes. | ||
// Meanwhile, our std::vector implementation consumes additionally 4 * m bytes | ||
// For our typical usage (i.e. Chrome) we see m = ~4n, so we use 16 * n bytes of | ||
// extra contiguous memory during computation. Assuming memory fragmentation | ||
// would not be an issue, this is much better than using std::map. | ||
void LabelManager::Read(RvaVisitor* rva_visitor) { | ||
// Write all values in |rva_visitor| to |rvas|. | ||
size_t num_rva = rva_visitor->Remaining(); | ||
std::vector<RVA> rvas(num_rva); | ||
for (size_t i = 0; i < num_rva; ++i, rva_visitor->Next()) | ||
rvas[i] = rva_visitor->Get(); | ||
|
||
// Sort |rvas|, then count the number of distinct values. | ||
using CRV = ConsecutiveRangeVisitor<std::vector<RVA>::iterator>; | ||
std::sort(rvas.begin(), rvas.end()); | ||
size_t num_distinct_rva = 0; | ||
for (CRV it(rvas.begin(), rvas.end()); it.has_more(); it.advance()) | ||
++num_distinct_rva; | ||
|
||
// Reserve space for |labels_|, populate with sorted RVA and repeats. | ||
DCHECK(labels_.empty()); | ||
labels_.reserve(num_distinct_rva); | ||
for (CRV it(rvas.begin(), rvas.end()); it.has_more(); it.advance()) { | ||
labels_.push_back(Label(*it.cur())); | ||
base::CheckedNumeric<uint32> count = it.repeat(); | ||
labels_.back().count_ = count.ValueOrDie(); | ||
} | ||
} | ||
|
||
void LabelManager::RemoveUnderusedLabels(int32 count_threshold) { | ||
if (count_threshold <= 0) | ||
return; | ||
labels_.erase(std::remove_if(labels_.begin(), labels_.end(), | ||
[count_threshold](const Label& label) { | ||
return label.count_ < count_threshold; | ||
}), | ||
labels_.end()); | ||
// Not shrinking |labels_|, since this may cause reallocation. | ||
} | ||
|
||
// Uses binary search to find |rva|. | ||
Label* LabelManager::Find(RVA rva) { | ||
auto it = std::lower_bound( | ||
labels_.begin(), labels_.end(), Label(rva), | ||
[](const Label& l1, const Label& l2) { return l1.rva_ < l2.rva_; }); | ||
return it == labels_.end() || it->rva_ != rva ? nullptr : &(*it); | ||
} | ||
|
||
} // namespace courgette |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright 2015 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 COURGETTE_LABEL_MANAGER_H_ | ||
#define COURGETTE_LABEL_MANAGER_H_ | ||
|
||
#include <vector> | ||
|
||
#include "base/macros.h" | ||
#include "courgette/image_utils.h" | ||
|
||
namespace courgette { | ||
|
||
// A container to store and manage Label instances. A key consideration is peak | ||
// memory usage reduction. To this end we preallocate Label instances in bulk, | ||
// and carefully control transient memory usage when initializing Labels. | ||
class LabelManager { | ||
public: | ||
// An adaptor to sequentially traverse multiple RVAs. This is useful for RVA | ||
// translation without extra storage. For example, we might have a stored list | ||
// of RVA locations, but we want to traverse the matching RVA targets. | ||
class RvaVisitor { | ||
public: | ||
virtual ~RvaVisitor(); | ||
|
||
// Returns the number of remaining RVAs to visit. | ||
virtual size_t Remaining() const = 0; | ||
|
||
// Returns the current RVA. | ||
virtual RVA Get() const = 0; | ||
|
||
// Advances to the next RVA. | ||
virtual void Next() = 0; | ||
}; | ||
|
||
LabelManager(); | ||
virtual ~LabelManager(); | ||
|
||
// Initializes |labels_| using RVAs from |rva_visitor|. Each distinct RVA from | ||
// |rva_visitor| yields a Label with |rva_| assigned as the RVA, and |count_| | ||
// assigned as the repeat. | ||
void Read(RvaVisitor* rva_visitor); | ||
|
||
// Removes |labels_| elements whose |count_| is less than |count_threshold|. | ||
void RemoveUnderusedLabels(int32 count_threshold); | ||
|
||
// Efficiently searches for a Label that targets |rva|. Returns the pointer to | ||
// the stored Label instance if found, or null otherwise. | ||
Label* Find(RVA rva); | ||
|
||
// TODO(huangs): Move AssignRemainingIndexes() here. | ||
|
||
protected: | ||
// The main list of Label instances, sorted by the |rva_| member. | ||
std::vector<Label> labels_; | ||
|
||
private: | ||
DISALLOW_COPY_AND_ASSIGN(LabelManager); | ||
}; | ||
|
||
} // namespace courgette | ||
|
||
#endif // COURGETTE_LABEL_MANAGER_H_ |
Oops, something went wrong.