Commit d2277a1c authored by Hayato Ito's avatar Hayato Ito Committed by Commit Bot

Make flat tree traversal faster

Current implementation of flat tree traversal is not cheap when traversing host's children.
See http://crbug.com/906494 for details.

There was an attempt to make it faster in v1, https://crrev.com/c/1328622, however, this idea was
already implemented in v0, and this attempt actually caused a perf regression in some cases
(https://crbug.com/905197).

This CL makes flat tree traversal for host's children much faster in a different approach.
The basic idea is:

1. Let NodeRareData has additional pointers, as FlatTreeNodeData, and set pointers at the timing of
   slot assignment recalc.
2. At flat tree traversal, use these pointers to skip hashmap lookup.

In this CL, |next| and |previous| pointers for assigned nodes are used in flat tree traversal.
Thanks to that, flat tree traversal no longer lookup node-to-index hashmap, nor do a liner search
there.

These additional pointers are only set for host's children. There shouldn't be additional cost
for other cases.

In a follow-up CL, I'll use FlatTreeNodeData's |assigned_slot| to make flat tree traversal and
other operations much faster again. Since this task might be complicated, that can be done in
another CL.

BUG: 906494

Change-Id: I617da70371d6733050b7dc754d3b40c88c090c79
Reviewed-on: https://chromium-review.googlesource.com/c/1337225
Commit-Queue: Hayato Ito <hayato@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609212}
parent e1d1f67d
......@@ -139,6 +139,8 @@ blink_core_sources("dom") {
"events/window_event_context.h",
"first_letter_pseudo_element.cc",
"first_letter_pseudo_element.h",
"flat_tree_node_data.cc",
"flat_tree_node_data.h",
"flat_tree_traversal.cc",
"flat_tree_traversal.h",
"frame_request_callback_collection.cc",
......
// Copyright 2018 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 "third_party/blink/renderer/core/dom/flat_tree_node_data.h"
#include "third_party/blink/renderer/core/html/html_slot_element.h"
namespace blink {
void FlatTreeNodeData::Trace(Visitor* visitor) {
visitor->Trace(assigned_slot_);
visitor->Trace(previous_in_assigned_nodes_);
visitor->Trace(next_in_assigned_nodes_);
}
} // namespace blink
// Copyright 2018 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 THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_NODE_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_NODE_DATA_H_
#include "base/macros.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink {
class Node;
class HTMLSlotElement;
class FlatTreeNodeData final : public GarbageCollected<FlatTreeNodeData> {
public:
FlatTreeNodeData() {}
void Trace(Visitor*);
private:
void SetAssignedSlot(HTMLSlotElement* assigned_slot) {
assigned_slot_ = assigned_slot;
}
void SetPreviousInAssignedNodes(Node* previous) {
previous_in_assigned_nodes_ = previous;
}
void SetNextInAssignedNodes(Node* next) { next_in_assigned_nodes_ = next; }
HTMLSlotElement* AssignedSlot() { return assigned_slot_; }
Node* PreviousInAssignedNodes() { return previous_in_assigned_nodes_; }
Node* NextInAssignedNodes() { return next_in_assigned_nodes_; }
friend class HTMLSlotElement;
WeakMember<HTMLSlotElement> assigned_slot_;
WeakMember<Node> previous_in_assigned_nodes_;
WeakMember<Node> next_in_assigned_nodes_;
DISALLOW_COPY_AND_ASSIGN(FlatTreeNodeData);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_FLAT_TREE_NODE_DATA_H_
......@@ -114,6 +114,8 @@ Node* FlatTreeTraversal::TraverseSiblings(const Node& node,
Node* FlatTreeTraversal::TraverseSiblingsForV1HostChild(
const Node& node,
TraversalDirection direction) {
// TODO(crbug.com/906494): Use flat_tree_node_data::assigned_slot to avoid
// hashmap lookup
HTMLSlotElement* slot = node.AssignedSlot();
if (!slot)
return nullptr;
......
......@@ -1250,6 +1250,16 @@ void Node::ClearNodeLists() {
RareData()->ClearNodeLists();
}
FlatTreeNodeData& Node::EnsureFlatTreeNodeData() {
return EnsureRareData().EnsureFlatTreeNodeData();
}
FlatTreeNodeData& Node::GetFlatTreeNodeData() const {
DCHECK(HasRareData());
DCHECK(RareData()->GetFlatTreeNodeData());
return *RareData()->GetFlatTreeNodeData();
}
bool Node::IsDescendantOf(const Node* other) const {
// Return true if other is an ancestor of this, otherwise false
if (!other || !other->hasChildren() || isConnected() != other->isConnected())
......
......@@ -51,6 +51,7 @@ class Element;
class Event;
class EventDispatchHandlingState;
class ExceptionState;
class FlatTreeNodeData;
class GetRootNodeOptions;
class HTMLQualifiedName;
class HTMLSlotElement;
......@@ -776,6 +777,10 @@ class CORE_EXPORT Node : public EventTarget {
NodeListsNodeData* NodeLists();
void ClearNodeLists();
// EnsureFlatTreeNodeData() must be called beforehand
FlatTreeNodeData& GetFlatTreeNodeData() const;
FlatTreeNodeData& EnsureFlatTreeNodeData();
virtual bool WillRespondToMouseMoveEvents();
virtual bool WillRespondToMouseClickEvents();
virtual bool WillRespondToTouchEvents();
......
......@@ -33,6 +33,7 @@
#include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/element_rare_data.h"
#include "third_party/blink/renderer/core/dom/flat_tree_node_data.h"
#include "third_party/blink/renderer/core/dom/mutation_observer_registration.h"
#include "third_party/blink/renderer/core/dom/node_lists_node_data.h"
#include "third_party/blink/renderer/core/page/page.h"
......@@ -43,7 +44,7 @@ namespace blink {
struct SameSizeAsNodeRareData {
void* pointer_;
Member<void*> willbe_member_[2];
Member<void*> willbe_member_[3];
unsigned bitfields_;
};
......@@ -83,6 +84,7 @@ void NodeMutationObserverData::RemoveRegistration(
void NodeRareData::TraceAfterDispatch(blink::Visitor* visitor) {
visitor->Trace(mutation_observer_data_);
visitor->Trace(flat_tree_node_data_);
// Do not keep empty NodeListsNodeData objects around.
if (node_lists_ && node_lists_->IsEmpty())
node_lists_.Clear();
......@@ -114,6 +116,12 @@ NodeListsNodeData& NodeRareData::CreateNodeLists() {
return *node_lists_;
}
FlatTreeNodeData& NodeRareData::EnsureFlatTreeNodeData() {
if (!flat_tree_node_data_)
flat_tree_node_data_ = MakeGarbageCollected<FlatTreeNodeData>();
return *flat_tree_node_data_;
}
// Ensure the 10 bits reserved for the connected_frame_count_ cannot overflow.
static_assert(Page::kMaxNumberOfFrames <
(1 << NodeRareData::kConnectedFrameCountBits),
......
......@@ -32,6 +32,7 @@ namespace blink {
class ComputedStyle;
enum class DynamicRestyleFlags;
enum class ElementFlags;
class FlatTreeNodeData;
class LayoutObject;
class MutationObserverRegistration;
class NodeListsNodeData;
......@@ -134,6 +135,9 @@ class NodeRareData : public GarbageCollectedFinalized<NodeRareData>,
return *node_lists_;
}
FlatTreeNodeData* GetFlatTreeNodeData() const { return flat_tree_node_data_; }
FlatTreeNodeData& EnsureFlatTreeNodeData();
NodeMutationObserverData* MutationObserverData() {
return mutation_observer_data_.Get();
}
......@@ -197,6 +201,7 @@ class NodeRareData : public GarbageCollectedFinalized<NodeRareData>,
TraceWrapperMember<NodeListsNodeData> node_lists_;
TraceWrapperMember<NodeMutationObserverData> mutation_observer_data_;
Member<FlatTreeNodeData> flat_tree_node_data_;
unsigned connected_frame_count_ : kConnectedFrameCountBits;
unsigned element_flags_ : kNumberOfElementFlags;
......
......@@ -229,7 +229,7 @@ void SlotAssignment::RecalcAssignment() {
needs_assignment_recalc_ = false;
for (Member<HTMLSlotElement> slot : Slots())
slot->ClearAssignedNodes();
slot->WillRecalcAssignedNodes();
const bool is_user_agent = owner_->IsUserAgent();
......@@ -266,10 +266,15 @@ void SlotAssignment::RecalcAssignment() {
}
}
if (slot)
if (slot) {
slot->AppendAssignedNode(child);
else
} else {
// TODO(crbug.com/906494) Clear child's flat_tree_node_data here, which
// should be useful for shortcut in flat tree traversal later; we can skip
// hashmap lookup (node->slot) there if we know the node doesn't have
// an assigned slot.
child.LazyReattachIfAttached();
}
}
if (owner_->isConnected()) {
......@@ -279,7 +284,7 @@ void SlotAssignment::RecalcAssignment() {
}
for (auto& slot : Slots())
slot->RecalcFlatTreeChildren();
slot->DidRecalcAssignedNodes();
}
const HeapVector<Member<HTMLSlotElement>>& SlotAssignment::Slots() {
......
......@@ -34,6 +34,7 @@
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/flat_tree_node_data.h"
#include "third_party/blink/renderer/core/dom/mutation_observer.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
......@@ -184,13 +185,11 @@ void HTMLSlotElement::assign(HeapVector<Member<Node>> nodes) {
void HTMLSlotElement::AppendAssignedNode(Node& host_child) {
DCHECK(host_child.IsSlotable());
assigned_nodes_index_.insert(&host_child, assigned_nodes_.size());
assigned_nodes_.push_back(&host_child);
}
void HTMLSlotElement::ClearAssignedNodes() {
assigned_nodes_.clear();
assigned_nodes_index_.clear();
}
void HTMLSlotElement::ClearAssignedNodesAndFlatTreeChildren() {
......@@ -198,6 +197,22 @@ void HTMLSlotElement::ClearAssignedNodesAndFlatTreeChildren() {
flat_tree_children_.clear();
}
void HTMLSlotElement::UpdateFlatTreeNodeDataForAssignedNodes() {
Node* previous = nullptr;
for (auto& current : assigned_nodes_) {
FlatTreeNodeData& flat_tree_node_data = current->EnsureFlatTreeNodeData();
flat_tree_node_data.SetAssignedSlot(this);
flat_tree_node_data.SetPreviousInAssignedNodes(previous);
if (previous) {
previous->GetFlatTreeNodeData().SetNextInAssignedNodes(current);
}
previous = current;
}
if (previous) {
previous->GetFlatTreeNodeData().SetNextInAssignedNodes(nullptr);
}
}
void HTMLSlotElement::RecalcFlatTreeChildren() {
DCHECK(SupportsAssignment());
......@@ -225,23 +240,15 @@ void HTMLSlotElement::DispatchSlotChangeEvent() {
Node* HTMLSlotElement::AssignedNodeNextTo(const Node& node) const {
DCHECK(SupportsAssignment());
ContainingShadowRoot()->GetSlotAssignment().RecalcAssignment();
auto it = assigned_nodes_index_.find(&node);
DCHECK(it != assigned_nodes_index_.end());
unsigned index = it->value;
if (index + 1 == assigned_nodes_.size())
return nullptr;
return assigned_nodes_[index + 1].Get();
DCHECK(assigned_nodes_.Contains(node));
return node.GetFlatTreeNodeData().NextInAssignedNodes();
}
Node* HTMLSlotElement::AssignedNodePreviousTo(const Node& node) const {
DCHECK(SupportsAssignment());
ContainingShadowRoot()->GetSlotAssignment().RecalcAssignment();
auto it = assigned_nodes_index_.find(&node);
DCHECK(it != assigned_nodes_index_.end());
unsigned index = it->value;
if (index == 0)
return nullptr;
return assigned_nodes_[index - 1].Get();
DCHECK(assigned_nodes_.Contains(node));
return node.GetFlatTreeNodeData().PreviousInAssignedNodes();
}
AtomicString HTMLSlotElement::GetName() const {
......@@ -556,7 +563,6 @@ int HTMLSlotElement::tabIndex() const {
void HTMLSlotElement::Trace(blink::Visitor* visitor) {
visitor->Trace(assigned_nodes_);
visitor->Trace(assigned_nodes_index_);
visitor->Trace(flat_tree_children_);
visitor->Trace(assigned_nodes_candidates_);
HTMLElement::Trace(visitor);
......
......@@ -68,10 +68,14 @@ class CORE_EXPORT HTMLSlotElement final : public HTMLElement {
Node* AssignedNodePreviousTo(const Node&) const;
void AppendAssignedNode(Node&);
void ClearAssignedNodes();
const HeapVector<Member<Node>> FlattenedAssignedNodes();
void RecalcFlatTreeChildren();
void WillRecalcAssignedNodes() { ClearAssignedNodes(); }
void DidRecalcAssignedNodes() {
UpdateFlatTreeNodeDataForAssignedNodes();
RecalcFlatTreeChildren();
}
void AttachLayoutTree(AttachContext&) final;
void DetachLayoutTree(const AttachContext& = AttachContext()) final;
......@@ -134,10 +138,12 @@ class CORE_EXPORT HTMLSlotElement final : public HTMLElement {
const HeapVector<Member<Node>>& GetDistributedNodes();
void RecalcFlatTreeChildren();
void UpdateFlatTreeNodeDataForAssignedNodes();
void ClearAssignedNodes();
void ClearAssignedNodesAndFlatTreeChildren();
HeapVector<Member<Node>> assigned_nodes_;
HeapHashMap<Member<const Node>, unsigned> assigned_nodes_index_;
HeapVector<Member<Node>> flat_tree_children_;
bool slotchange_event_enqueued_ = false;
......
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