Simple line layout(regression): Calling innerText on RenderFlow with multiple childre...
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Apr 2015 18:57:33 +0000 (18:57 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Apr 2015 18:57:33 +0000 (18:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143554

Reviewed by Antti Koivisto.

Initialize render flow's segments only when the render flow changes in TextIterator.
The included performance test shows 6x speedup. (from ~10 runs/sec to ~60 runs/sec)

PerformanceTests:

* Layout/simple-line-layout-innertext.html: Added.

Source/WebCore:

Test: PerformanceTests/Layout/simple-line-layout-innertext.html.

* editing/TextIterator.cpp:
(WebCore::TextIterator::handleTextNode):
* editing/TextIterator.h:
* rendering/SimpleLineLayoutFlowContents.cpp: Instruments log shows that vector's expandCapacity could be expensive when flow has large amount of children.
(WebCore::SimpleLineLayout::initializeSegments):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182604 268f45cc-cd09-0410-ab3c-d52691b4dbfc

PerformanceTests/ChangeLog
PerformanceTests/Layout/simple-line-layout-innertext.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/editing/TextIterator.cpp
Source/WebCore/editing/TextIterator.h
Source/WebCore/rendering/SimpleLineLayoutFlowContents.cpp
Source/WebCore/rendering/SimpleLineLayoutResolver.cpp
Source/WebCore/rendering/SimpleLineLayoutResolver.h

index 8615dd4..44777a2 100644 (file)
@@ -1,3 +1,15 @@
+2015-04-09  Zalan Bujtas  <zalan@apple.com>
+
+        Simple line layout(regression): Calling innerText on RenderFlow with multiple children is slow.
+        https://bugs.webkit.org/show_bug.cgi?id=143554
+
+        Reviewed by Antti Koivisto.
+
+        Initialize render flow's segments only when the render flow changes in TextIterator.
+        The included performance test shows 6x speedup. (from ~10 runs/sec to ~60 runs/sec)
+
+        * Layout/simple-line-layout-innertext.html: Added.
+
 2015-03-09  Chris Dumez  <cdumez@apple.com>
 
         [CG] Have Canvas use the IOSurfacePool
diff --git a/PerformanceTests/Layout/simple-line-layout-innertext.html b/PerformanceTests/Layout/simple-line-layout-innertext.html
new file mode 100644 (file)
index 0000000..e6a74eb
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>innerText performance test with simple line layout.</title>
+    <script src="../resources/runner.js"></script>
+</head>
+<body>
+    <pre id="log"></pre>
+    <div id="target" style="width: 300px;"></div>
+    <div id="result"></div>
+    <script>
+        var target = document.getElementById("target");
+        var result = document.getElementById("result");
+        for (i = 0; i < 500; ++i)
+            target.appendChild(document.createTextNode("foobar"));
+
+        function test() {
+            for (i = 0; i < 10; ++i)
+                result.innerText += target.innerText;
+            result.innerText = "";
+        }
+        PerfTestRunner.measureRunsPerSecond({ run: test });
+    </script>
+</body>
+</html>
index 7ef58b5..0f108d4 100644 (file)
@@ -1,3 +1,21 @@
+2015-04-09  Zalan Bujtas  <zalan@apple.com>
+
+        Simple line layout(regression): Calling innerText on RenderFlow with multiple children is slow.
+        https://bugs.webkit.org/show_bug.cgi?id=143554
+
+        Reviewed by Antti Koivisto.
+
+        Initialize render flow's segments only when the render flow changes in TextIterator.
+        The included performance test shows 6x speedup. (from ~10 runs/sec to ~60 runs/sec)
+
+        Test: PerformanceTests/Layout/simple-line-layout-innertext.html.
+
+        * editing/TextIterator.cpp:
+        (WebCore::TextIterator::handleTextNode):
+        * editing/TextIterator.h:
+        * rendering/SimpleLineLayoutFlowContents.cpp: Instruments log shows that vector's expandCapacity could be expensive when flow has large amount of children.
+        (WebCore::SimpleLineLayout::initializeSegments):
+
 2015-04-09  Chris Dumez  <cdumez@apple.com>
 
         [WK2][iOS] editorState() should not cause a synchronous layout
index b6ac079..b15d5ad 100644 (file)
@@ -548,20 +548,18 @@ bool TextIterator::handleTextNode()
             return true;
         // Use the simple layout runs to iterate over the text content.
         ASSERT(renderer.parent() && is<RenderBlockFlow>(renderer.parent()));
-        const auto& blockFlow = downcast<RenderBlockFlow>(*renderer.parent());
-        SimpleLineLayout::RunResolver runResolver(blockFlow, *layout);
-        auto range = runResolver.rangeForRenderer(renderer);
         unsigned endPosition = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
-        // Simple line layout run positions are all absolute to the parent flow.
-        // Offsetting is required when multiple renderers are present.
-        if (previousTextNode && previousTextNode != &textNode) {
-            const RenderObject& previousRenderer = *previousTextNode->renderer();
-            if (previousRenderer.parent() != &blockFlow)
-                m_previousTextLengthInFlow = 0;
-            else
-                m_previousTextLengthInFlow += previousTextNode->renderer()->text()->length();
+        const auto& blockFlow = downcast<RenderBlockFlow>(*renderer.parent());
+        if (!m_flowRunResolverCache || &m_flowRunResolverCache->flow() != &blockFlow) {
+            m_flowRunResolverCache = std::make_unique<SimpleLineLayout::RunResolver>(blockFlow, *layout);
+            m_previousTextLengthInFlow = 0;
+        } else if (previousTextNode && previousTextNode != &textNode) {
+            // Simple line layout run positions are all absolute to the parent flow.
+            // Offsetting is required when multiple renderers are present.
+            m_previousTextLengthInFlow += previousTextNode->renderer()->text()->length();
         }
         // Skip to m_offset position.
+        auto range = m_flowRunResolverCache->rangeForRenderer(renderer);
         auto it = range.begin();
         auto end = range.end();
         while (it != end && (*it).end() <= (static_cast<unsigned>(m_offset) + m_previousTextLengthInFlow))
index 63cf3d5..af2c386 100644 (file)
@@ -39,6 +39,9 @@ namespace WebCore {
 class InlineTextBox;
 class RenderText;
 class RenderTextFragment;
+namespace SimpleLineLayout {
+class RunResolver;
+}
 
 WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
 WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
@@ -166,6 +169,8 @@ private:
     // Used to do simple line layout run logic.
     bool m_nextRunNeedsWhitespace { false };
     unsigned m_previousTextLengthInFlow { 0 };
+    std::unique_ptr<SimpleLineLayout::RunResolver> m_flowRunResolverCache;
+
     // Used when text boxes are out of order (Hebrew/Arabic with embedded LTR text)
     Vector<InlineTextBox*> m_sortedTextBoxes;
     size_t m_sortedTextBoxesPosition;
index 16cb09e..c29402d 100644 (file)
@@ -35,7 +35,13 @@ namespace SimpleLineLayout {
 
 static Vector<FlowContents::Segment> initializeSegments(const RenderBlockFlow& flow)
 {
-    Vector<FlowContents::Segment, 8> segments;
+
+    unsigned numberOfChildren = 0;
+    auto children = childrenOfType<RenderText>(flow);
+    for (auto it = children.begin(), end = children.end(); it != end; ++it)
+        ++numberOfChildren;
+    Vector<FlowContents::Segment> segments;
+    segments.reserveCapacity(numberOfChildren);
     unsigned startPosition = 0;
     for (auto& textChild : childrenOfType<RenderText>(flow)) {
         unsigned textLength = textChild.text()->length();
index b23b475..fa6c96a 100644 (file)
@@ -116,7 +116,8 @@ RunResolver::Iterator& RunResolver::Iterator::advanceLines(unsigned lineCount)
 }
 
 RunResolver::RunResolver(const RenderBlockFlow& flow, const Layout& layout)
-    : m_layout(layout)
+    : m_flowRenderer(flow)
+    , m_layout(layout)
     , m_flowContents(flow)
     , m_lineHeight(lineHeightFromFlow(flow))
     , m_baseline(baselineFromFlow(flow))
index 0a98d1f..37bc98f 100644 (file)
@@ -99,6 +99,7 @@ public:
 
     RunResolver(const RenderBlockFlow&, const Layout&);
 
+    const RenderBlockFlow& flow() const { return m_flowRenderer; }
     Iterator begin() const;
     Iterator end() const;
 
@@ -109,6 +110,7 @@ private:
     enum class IndexType { First, Last };
     unsigned lineIndexForHeight(LayoutUnit, IndexType) const;
 
+    const RenderBlockFlow& m_flowRenderer;
     const Layout& m_layout;
     const FlowContents m_flowContents;
     const LayoutUnit m_lineHeight;