LibWeb: Add a 1-entry lookup cache to FormattingState

This makes repeated lookups of the state for the same box much faster
by bypassing the HashMap.
This commit is contained in:
Andreas Kling 2022-07-06 14:56:10 +02:00
parent e7370443f2
commit 83a6be593c
Notes: sideshowbarker 2024-07-17 09:38:35 +09:00
2 changed files with 39 additions and 16 deletions

View file

@ -12,32 +12,48 @@ namespace Web::Layout {
FormattingState::NodeState& FormattingState::get_mutable(NodeWithStyleAndBoxModelMetrics const& box)
{
if (auto it = nodes.find(&box); it != nodes.end())
return *it->value;
if (m_lookup_cache.box == &box && m_lookup_cache.is_mutable)
return *m_lookup_cache.state;
for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end()) {
auto cow_node_state = adopt_own(*new NodeState(*it->value));
auto* cow_node_state_ptr = cow_node_state.ptr();
nodes.set(&box, move(cow_node_state));
return *cow_node_state_ptr;
auto& node_state = [&]() -> NodeState& {
if (auto it = nodes.find(&box); it != nodes.end())
return *it->value;
for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end()) {
auto cow_node_state = adopt_own(*new NodeState(*it->value));
auto* cow_node_state_ptr = cow_node_state.ptr();
nodes.set(&box, move(cow_node_state));
return *cow_node_state_ptr;
}
}
}
return *nodes.ensure(&box, [] { return adopt_own(*new NodeState); });
return *nodes.ensure(&box, [] { return adopt_own(*new NodeState); });
}();
m_lookup_cache = LookupCache { .box = &box, .state = &node_state, .is_mutable = true };
return node_state;
}
FormattingState::NodeState const& FormattingState::get(NodeWithStyleAndBoxModelMetrics const& box) const
{
if (auto it = nodes.find(&box); it != nodes.end())
return *it->value;
if (m_lookup_cache.box == &box)
return *m_lookup_cache.state;
for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end())
auto& node_state = [&]() -> NodeState const& {
if (auto it = nodes.find(&box); it != nodes.end())
return *it->value;
}
return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_own(*new NodeState); });
for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end())
return *it->value;
}
return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_own(*new NodeState); });
}();
const_cast<FormattingState*>(this)->m_lookup_cache = LookupCache { .box = &box, .state = const_cast<NodeState*>(&node_state), .is_mutable = false };
return node_state;
}
void FormattingState::commit()

View file

@ -111,6 +111,13 @@ struct FormattingState {
FormattingState const* m_parent { nullptr };
FormattingState const& m_root;
struct LookupCache {
NodeWithStyleAndBoxModelMetrics const* box { nullptr };
NodeState* state { nullptr };
bool is_mutable { false };
};
LookupCache m_lookup_cache;
};
Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&);