mirror of
https://github.com/SerenityOS/serenity.git
synced 2026-11-07 04:29:30 -10:00
LibWeb: Move initial creation of Unicode segmenters to the Document
The expensive part of creating a segmenter is doing the locale and UCD
data lookups at creation time. Instead of doing this once per text node,
cache the segmenters on the document, and clone them as needed (cloning
is much, much cheaper).
On a profile loading Ladybird's GitHub repo, the following hot methods
changed as follows:
ChunkIterator ctor: 6.08% -> 0.21%
Segmenter factory: 5.86% -> 0%
Segmenter clone: N/A -> 0.09%
(cherry picked from commit 5d7175874258f00763adcced230149dd1379e4a6;
mended as usual for Unicode::Segmenter -> Locale::Segmenter, and also
to resolve minor conflicts due to serenity not yet having
LadybirdBrowser/ladybird#1106. Also, due to serenity not using ICU,
this doesn't have a big performance effect over here, but it still
makes future cherry-picks easier)
This commit is contained in:
1 parent
833147b7a6
commit
bb8b24eef7
8 files changed
+52
-18
No files matched your search
@@ -157,20 +157,20 @@ WebIDL::ExceptionOr<void> CharacterData::delete_data(size_t offset, size_t count
|
||||
return replace_data(offset, count, String {});
|
||||
}
|
||||
|
||||
Locale::Segmenter& CharacterData::grapheme_segmenter()
|
||||
Locale::Segmenter& CharacterData::grapheme_segmenter() const
|
||||
{
|
||||
if (!m_grapheme_segmenter) {
|
||||
m_grapheme_segmenter = Locale::Segmenter::create(Locale::SegmenterGranularity::Grapheme);
|
||||
m_grapheme_segmenter = document().grapheme_segmenter().clone();
|
||||
m_grapheme_segmenter->set_segmented_text(m_data);
|
||||
}
|
||||
|
||||
return *m_grapheme_segmenter;
|
||||
}
|
||||
|
||||
Locale::Segmenter& CharacterData::word_segmenter()
|
||||
Locale::Segmenter& CharacterData::word_segmenter() const
|
||||
{
|
||||
if (!m_word_segmenter) {
|
||||
m_word_segmenter = Locale::Segmenter::create(Locale::SegmenterGranularity::Word);
|
||||
m_word_segmenter = document().word_segmenter().clone();
|
||||
m_word_segmenter->set_segmented_text(m_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ public:
|
||||
WebIDL::ExceptionOr<void> delete_data(size_t offset_in_utf16_code_units, size_t count_in_utf16_code_units);
|
||||
WebIDL::ExceptionOr<void> replace_data(size_t offset_in_utf16_code_units, size_t count_in_utf16_code_units, String const&);
|
||||
|
||||
Locale::Segmenter& grapheme_segmenter();
|
||||
Locale::Segmenter& word_segmenter();
|
||||
Locale::Segmenter& grapheme_segmenter() const;
|
||||
Locale::Segmenter& word_segmenter() const;
|
||||
|
||||
protected:
|
||||
CharacterData(Document&, NodeType, String const&);
|
||||
@@ -51,8 +51,8 @@ protected:
|
||||
private:
|
||||
String m_data;
|
||||
|
||||
OwnPtr<Locale::Segmenter> m_grapheme_segmenter;
|
||||
OwnPtr<Locale::Segmenter> m_word_segmenter;
|
||||
mutable OwnPtr<Locale::Segmenter> m_grapheme_segmenter;
|
||||
mutable OwnPtr<Locale::Segmenter> m_word_segmenter;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
#include <LibLocale/Segmenter.h>
|
||||
#include <LibURL/Origin.h>
|
||||
#include <LibURL/Parser.h>
|
||||
#include <LibWeb/Animations/Animation.h>
|
||||
@@ -5556,4 +5557,18 @@ void Document::set_cached_navigable(JS::GCPtr<HTML::Navigable> navigable)
|
||||
m_cached_navigable = navigable.ptr();
|
||||
}
|
||||
|
||||
Locale::Segmenter& Document::grapheme_segmenter() const
|
||||
{
|
||||
if (!m_grapheme_segmenter)
|
||||
m_grapheme_segmenter = Locale::Segmenter::create(Locale::SegmenterGranularity::Grapheme);
|
||||
return *m_grapheme_segmenter;
|
||||
}
|
||||
|
||||
Locale::Segmenter& Document::word_segmenter() const
|
||||
{
|
||||
if (!m_word_segmenter)
|
||||
m_word_segmenter = Locale::Segmenter::create(Locale::SegmenterGranularity::Word);
|
||||
return *m_word_segmenter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibURL/Origin.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibUnicode/Forward.h>
|
||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||
#include <LibWeb/CSS/StyleSheetList.h>
|
||||
#include <LibWeb/Cookie/Cookie.h>
|
||||
@@ -709,6 +710,9 @@ public:
|
||||
JS::GCPtr<HTML::Navigable> cached_navigable();
|
||||
void set_cached_navigable(JS::GCPtr<HTML::Navigable>);
|
||||
|
||||
Locale::Segmenter& grapheme_segmenter() const;
|
||||
Locale::Segmenter& word_segmenter() const;
|
||||
|
||||
protected:
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
@@ -986,6 +990,9 @@ private:
|
||||
|
||||
// NOTE: This is WeakPtr, not GCPtr, on purpose. We don't want the document to keep some old detached navigable alive.
|
||||
WeakPtr<HTML::Navigable> m_cached_navigable;
|
||||
|
||||
mutable OwnPtr<Locale::Segmenter> m_grapheme_segmenter;
|
||||
mutable OwnPtr<Locale::Segmenter> m_word_segmenter;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -368,7 +368,7 @@ void InlineLevelIterator::enter_text_node(Layout::TextNode const& text_node)
|
||||
.do_respect_linebreaks = do_respect_linebreaks,
|
||||
.is_first_chunk = true,
|
||||
.is_last_chunk = false,
|
||||
.chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), do_wrap_lines, do_respect_linebreaks, text_node.computed_values().font_list() },
|
||||
.chunk_iterator = TextNode::ChunkIterator { text_node, do_wrap_lines, do_respect_linebreaks },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -391,14 +391,23 @@ void TextNode::compute_text_for_rendering()
|
||||
m_text_for_rendering = MUST(builder.to_string());
|
||||
}
|
||||
|
||||
TextNode::ChunkIterator::ChunkIterator(String const& text, bool wrap_lines, bool respect_linebreaks, Gfx::FontCascadeList const& font_cascade_list)
|
||||
Locale::Segmenter& TextNode::grapheme_segmenter() const
|
||||
{
|
||||
if (!m_grapheme_segmenter) {
|
||||
m_grapheme_segmenter = document().grapheme_segmenter().clone();
|
||||
m_grapheme_segmenter->set_segmented_text(text_for_rendering());
|
||||
}
|
||||
|
||||
return *m_grapheme_segmenter;
|
||||
}
|
||||
|
||||
TextNode::ChunkIterator::ChunkIterator(TextNode const& text_node, bool wrap_lines, bool respect_linebreaks)
|
||||
: m_wrap_lines(wrap_lines)
|
||||
, m_respect_linebreaks(respect_linebreaks)
|
||||
, m_utf8_view(text)
|
||||
, m_font_cascade_list(font_cascade_list)
|
||||
, m_segmenter(Locale::Segmenter::create(Locale::SegmenterGranularity::Grapheme))
|
||||
, m_utf8_view(text_node.text_for_rendering())
|
||||
, m_font_cascade_list(text_node.computed_values().font_list())
|
||||
, m_grapheme_segmenter(text_node.grapheme_segmenter())
|
||||
{
|
||||
m_segmenter->set_segmented_text(text);
|
||||
}
|
||||
|
||||
static Gfx::GlyphRun::TextType text_type_for_code_point(u32 code_point)
|
||||
@@ -470,7 +479,7 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::next_without_peek()
|
||||
return *m_utf8_view.iterator_at_byte_offset_without_validation(m_current_index);
|
||||
};
|
||||
auto next_grapheme_boundary = [this]() {
|
||||
return m_segmenter->next_boundary(m_current_index).value_or(m_utf8_view.byte_length());
|
||||
return m_grapheme_segmenter.next_boundary(m_current_index).value_or(m_utf8_view.byte_length());
|
||||
};
|
||||
|
||||
auto code_point = current_code_point();
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
class ChunkIterator {
|
||||
public:
|
||||
ChunkIterator(String const& text, bool wrap_lines, bool respect_linebreaks, Gfx::FontCascadeList const&);
|
||||
ChunkIterator(TextNode const&, bool wrap_lines, bool respect_linebreaks);
|
||||
|
||||
Optional<Chunk> next();
|
||||
Optional<Chunk> peek(size_t);
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
Utf8View m_utf8_view;
|
||||
Gfx::FontCascadeList const& m_font_cascade_list;
|
||||
|
||||
NonnullOwnPtr<Locale::Segmenter> m_segmenter;
|
||||
Locale::Segmenter& m_grapheme_segmenter;
|
||||
size_t m_current_index { 0 };
|
||||
|
||||
Vector<Chunk> m_peek_queue;
|
||||
@@ -62,12 +62,15 @@ public:
|
||||
void invalidate_text_for_rendering();
|
||||
void compute_text_for_rendering();
|
||||
|
||||
Locale::Segmenter& grapheme_segmenter() const;
|
||||
|
||||
virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;
|
||||
|
||||
private:
|
||||
virtual bool is_text_node() const final { return true; }
|
||||
|
||||
Optional<String> m_text_for_rendering;
|
||||
mutable OwnPtr<Locale::Segmenter> m_grapheme_segmenter;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
||||
@@ -1229,7 +1229,7 @@ void EventHandler::update_selection_range_for_input_or_textarea()
|
||||
Locale::Segmenter& EventHandler::word_segmenter()
|
||||
{
|
||||
if (!m_word_segmenter)
|
||||
m_word_segmenter = Locale::Segmenter::create(Locale::SegmenterGranularity::Word);
|
||||
m_word_segmenter = m_navigable->active_document()->word_segmenter().clone();
|
||||
return *m_word_segmenter;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user