sync: Count nodes more efficiently

The GetTotalNodesCount() function is called sufficiently often that it
makes sense to spend some effort optimizing its implementation.

The current implementation uses some traversal functions that are fairly
slow, especially since GetSuccessorId() was made more expensive by the
implementation of UniquePositions.

This change moves the implementation into the syncable::Directory.  This
allows it to take advantage of the Directory's internal data structures
and avoid unnecessary lookups. 

BUG=248143,238621

Review URL: https://chromiumcodereview.appspot.com/15322003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202973 0039d316-1c4b-4281-b951-d872f2087c98
parent f7435c24
...@@ -220,31 +220,7 @@ void BaseNode::GetChildIds(std::vector<int64>* result) const { ...@@ -220,31 +220,7 @@ void BaseNode::GetChildIds(std::vector<int64>* result) const {
} }
int BaseNode::GetTotalNodeCount() const { int BaseNode::GetTotalNodeCount() const {
syncable::BaseTransaction* trans = GetTransaction()->GetWrappedTrans(); return GetEntry()->GetTotalNodeCount();
int count = 1; // Start with one to include the node itself.
std::stack<int64> stack;
stack.push(GetFirstChildId());
while (!stack.empty()) {
int64 handle = stack.top();
stack.pop();
if (handle == kInvalidId)
continue;
count++;
syncable::Entry entry(trans, syncable::GET_BY_HANDLE, handle);
if (!entry.good())
continue;
syncable::Id successor_id = entry.GetSuccessorId();
if (!successor_id.IsRoot())
stack.push(IdToMetahandle(trans, successor_id));
if (!entry.Get(syncable::IS_DIR))
continue;
syncable::Id child_id = entry.GetFirstChildId();
if (!child_id.IsRoot())
stack.push(IdToMetahandle(trans, child_id));
}
return count;
} }
DictionaryValue* BaseNode::GetSummaryAsValue() const { DictionaryValue* BaseNode::GetSummaryAsValue() const {
......
...@@ -321,6 +321,46 @@ bool Directory::GetChildHandlesByHandle( ...@@ -321,6 +321,46 @@ bool Directory::GetChildHandlesByHandle(
return true; return true;
} }
int Directory::GetTotalNodeCount(
BaseTransaction* trans,
EntryKernel* kernel) const {
if (!SyncAssert(this == trans->directory(), FROM_HERE,
"Directories don't match", trans))
return false;
int count = 1;
std::deque<const OrderedChildSet*> child_sets;
GetChildSetForKernel(trans, kernel, &child_sets);
while (!child_sets.empty()) {
const OrderedChildSet* set = child_sets.front();
child_sets.pop_front();
for (OrderedChildSet::const_iterator it = set->begin();
it != set->end(); ++it) {
count++;
GetChildSetForKernel(trans, *it, &child_sets);
}
}
return count;
}
void Directory::GetChildSetForKernel(
BaseTransaction* trans,
EntryKernel* kernel,
std::deque<const OrderedChildSet*>* child_sets) const {
if (!kernel->ref(IS_DIR))
return; // Not a directory => no children.
const OrderedChildSet* descendants =
kernel_->parent_child_index->GetChildren(kernel->ref(ID));
if (!descendants)
return; // This directory has no children.
// Add our children to the list of items to be traversed.
child_sets->push_back(descendants);
}
EntryKernel* Directory::GetRootEntry() { EntryKernel* Directory::GetRootEntry() {
return GetEntryById(Id()); return GetEntryById(Id());
} }
......
...@@ -321,6 +321,9 @@ class SYNC_EXPORT Directory { ...@@ -321,6 +321,9 @@ class SYNC_EXPORT Directory {
bool GetChildHandlesByHandle(BaseTransaction*, int64 handle, bool GetChildHandlesByHandle(BaseTransaction*, int64 handle,
ChildHandles* result); ChildHandles* result);
// Counts all items under the given node, including the node itself.
int GetTotalNodeCount(BaseTransaction*, EntryKernel* kernel_) const;
// Returns true iff |id| has children. // Returns true iff |id| has children.
bool HasChildren(BaseTransaction* trans, const Id& id); bool HasChildren(BaseTransaction* trans, const Id& id);
...@@ -454,6 +457,12 @@ class SYNC_EXPORT Directory { ...@@ -454,6 +457,12 @@ class SYNC_EXPORT Directory {
bool SafeToPurgeFromMemory(WriteTransaction* trans, bool SafeToPurgeFromMemory(WriteTransaction* trans,
const EntryKernel* const entry) const; const EntryKernel* const entry) const;
// A helper used by GetTotalNodeCount.
void GetChildSetForKernel(
BaseTransaction*,
EntryKernel* kernel_,
std::deque<const OrderedChildSet*>* child_sets) const;
Directory& operator = (const Directory&); Directory& operator = (const Directory&);
public: public:
......
...@@ -108,6 +108,10 @@ void Entry::GetChildHandles(std::vector<int64>* result) const { ...@@ -108,6 +108,10 @@ void Entry::GetChildHandles(std::vector<int64>* result) const {
dir()->GetChildHandlesById(basetrans_, Get(ID), result); dir()->GetChildHandlesById(basetrans_, Get(ID), result);
} }
int Entry::GetTotalNodeCount() const {
return dir()->GetTotalNodeCount(basetrans_, kernel_);
}
bool Entry::ShouldMaintainPosition() const { bool Entry::ShouldMaintainPosition() const {
return kernel_->ShouldMaintainPosition(); return kernel_->ShouldMaintainPosition();
} }
......
...@@ -108,6 +108,7 @@ class SYNC_EXPORT Entry { ...@@ -108,6 +108,7 @@ class SYNC_EXPORT Entry {
Id GetPredecessorId() const; Id GetPredecessorId() const;
Id GetSuccessorId() const; Id GetSuccessorId() const;
Id GetFirstChildId() const; Id GetFirstChildId() const;
int GetTotalNodeCount() const;
// Returns a vector of this node's children's handles. // Returns a vector of this node's children's handles.
// Clears |result| if there are no children. If this node is of a type that // Clears |result| if there are no children. If this node is of a type that
......
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