Title: [280193] trunk
Revision
280193
Author
[email protected]
Date
2021-07-22 12:18:45 -0700 (Thu, 22 Jul 2021)

Log Message

Micro-optimize innerHTML
https://bugs.webkit.org/show_bug.cgi?id=228142

Reviewed by Simon Fraser.

Source/WebCore:

No behavior change.

This patch does some micro optimizations revealed by the profiler when running some of Speedometer2 tests which intensively use innerHTML.
This offers improvement in jQuery-TodoMVC and Vanilla-ES2015-Babel-Webpack-TodoMVC since both are super innerHTML heavy benchmarks.

----------------------------------------------------------------------------------------------------------------------------------
|               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
----------------------------------------------------------------------------------------------------------------------------------
| Elm-TodoMVC                          |126.862500   |126.687500   |0.998621  | 0.673462                                         |
| VueJS-TodoMVC                        |27.775000    |27.645833    |0.995350  | 0.741588                                         |
| EmberJS-TodoMVC                      |129.350000   |129.129167   |0.998293  | 0.624196                                         |
| BackboneJS-TodoMVC                   |51.129167    |51.204167    |1.001467  | 0.716622                                         |
| Preact-TodoMVC                       |21.870833    |21.337500    |0.975614  | 0.217771                                         |
| AngularJS-TodoMVC                    |139.854167   |140.266667   |1.002950  | 0.489838                                         |
| Vanilla-ES2015-TodoMVC               |69.229167    |68.895833    |0.995185  | 0.238772                                         |
| Inferno-TodoMVC                      |68.391667    |68.266667    |0.998172  | 0.762281                                         |
| Flight-TodoMVC                       |77.979167    |78.166667    |1.002404  | 0.710324                                         |
| Angular2-TypeScript-TodoMVC          |39.741667    |39.966667    |1.005662  | 0.524123                                         |
| VanillaJS-TodoMVC                    |55.416667    |55.512500    |1.001729  | 0.781447                                         |
| jQuery-TodoMVC                       |268.812500   |266.966667   |0.993133  | 0.003384 (significant)                           |
| EmberJS-Debug-TodoMVC                |345.383333   |345.662500   |1.000808  | 0.695259                                         |
| React-TodoMVC                        |90.679167    |90.179167    |0.994486  | 0.067477                                         |
| React-Redux-TodoMVC                  |152.691667   |152.687500   |0.999973  | 0.991207                                         |
| Vanilla-ES2015-Babel-Webpack-TodoMVC |66.487500    |65.729167    |0.988594  | 0.000118 (significant)                           |
----------------------------------------------------------------------------------------------------------------------------------

a mean = 242.12319
b mean = 242.80485
pValue = 0.1992654128
(Bigger means are better.)
1.003 times better
Results ARE NOT significant

* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::insertTextNode):
* html/parser/HTMLConstructionSite.h:
* html/parser/HTMLDocumentParser.cpp:
(WebCore::HTMLDocumentParser::pumpTokenizerLoop): We do not need to call `shrinkToBestFit` in fragment parsing case since
we will discard HTMLToken soon.
* html/parser/HTMLMetaCharsetParser.cpp:
(WebCore::HTMLMetaCharsetParser::checkForMetaCharset):
* html/parser/HTMLPreloadScanner.cpp:
(WebCore::HTMLPreloadScanner::scan):
* html/parser/HTMLToken.h:
(WebCore::HTMLToken::clear): We found that these `clear` calls cause performance problem according to the Instruments: we
repeatedly use this Vector, and we repeatedly allocate and deallocate this Vector unnecessarily. We use `resize(0)` instead
to avoid this allocation and deallocation.
(WebCore::HTMLToken::shrinkToBestFit): But HTMLToken is kept so long, so at some point, we would like to make backing storage
small. So, we add shrinkToBestFit and we call it only after finishing batching of HTMLToken processing.
(WebCore::HTMLToken::beginStartTag):
(WebCore::HTMLToken::beginEndTag):
* html/parser/HTMLTokenizer.h:
(WebCore::HTMLTokenizer::shrinkToBestFit):
* html/parser/HTMLTreeBuilder.cpp:
(WebCore::HTMLTreeBuilder::ExternalCharacterTokenBuffer::characterPredicate):
(WebCore::HTMLTreeBuilder::insertPhoneNumberLink):
(WebCore::HTMLTreeBuilder::linkifyPhoneNumbers):
(WebCore::HTMLTreeBuilder::processCharacterBuffer):
(WebCore::HTMLTreeBuilder::processCharacterBufferForInBody):
(WebCore::HTMLTreeBuilder::defaultForInTableText):
(WebCore::HTMLTreeBuilder::processTokenInForeignContent):
(WebCore::HTMLTreeBuilder::processFakeCharacters): Deleted. It is dead code before this patch.
* html/parser/HTMLTreeBuilder.h:

Source/WTF:

* wtf/Vector.h:
(WTF::Malloc>::shrinkToBestFit): This shrinks the backing storage to "appropriate for the living Vector".
* wtf/text/AtomStringImpl.cpp:
(WTF::UCharBufferTranslator::equal): WTF::equal for String does not check hash. This is because computing hash is expensive
and we would like to avoid that if it is not necessary. But when inserting string into AtomStringTable, we can use hash value
since they must be already computed because of HashMap's requirement. So let's use it before calling WTF::equal.
(WTF::LCharBufferTranslator::equal):
(WTF::BufferFromStaticDataTranslator::equal):
* wtf/text/StringView.h:
(WTF::StringView::stripLeadingMatchedCharacters): Add this and use it in HTMLTreeBuilder.

Tools:

* TestWebKitAPI/Tests/WTF/StringView.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WTF/Vector.cpp:
(TestWebKitAPI::TEST):

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (280192 => 280193)


--- trunk/Source/WTF/ChangeLog	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WTF/ChangeLog	2021-07-22 19:18:45 UTC (rev 280193)
@@ -1,3 +1,21 @@
+2021-07-22  Yusuke Suzuki  <[email protected]>
+
+        Micro-optimize innerHTML
+        https://bugs.webkit.org/show_bug.cgi?id=228142
+
+        Reviewed by Simon Fraser.
+
+        * wtf/Vector.h:
+        (WTF::Malloc>::shrinkToBestFit): This shrinks the backing storage to "appropriate for the living Vector".
+        * wtf/text/AtomStringImpl.cpp:
+        (WTF::UCharBufferTranslator::equal): WTF::equal for String does not check hash. This is because computing hash is expensive
+        and we would like to avoid that if it is not necessary. But when inserting string into AtomStringTable, we can use hash value
+        since they must be already computed because of HashMap's requirement. So let's use it before calling WTF::equal.
+        (WTF::LCharBufferTranslator::equal):
+        (WTF::BufferFromStaticDataTranslator::equal):
+        * wtf/text/StringView.h:
+        (WTF::StringView::stripLeadingMatchedCharacters): Add this and use it in HTMLTreeBuilder.
+
 2021-07-22  Alex Christensen  <[email protected]>
 
         XHR.send(Document) should replace mismatched surrogates with replacement character before sending

Modified: trunk/Source/WTF/wtf/Vector.h (280192 => 280193)


--- trunk/Source/WTF/wtf/Vector.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WTF/wtf/Vector.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -768,6 +768,10 @@
     void shrinkCapacity(size_t newCapacity);
     void shrinkToFit() { shrinkCapacity(size()); }
 
+    // "shrinkToBestFit()" shrinks the capacity to fix the current size, while leaving sufficient capacity for more items.
+    // The upperbound of the capacity is that which would result from calling expandCapacity() with the current size.
+    void shrinkToBestFit();
+
     void clear() { shrinkCapacity(0); }
 
     template<typename U = T> Vector<U> isolatedCopy() const &;
@@ -1258,6 +1262,15 @@
 }
 
 template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
+void Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::shrinkToBestFit()
+{
+    size_t currentSize = size();
+    size_t newCapacity = currentSize ? std::max<size_t>(minCapacity, currentSize + currentSize / 4 + 1) : 0;
+    ASSERT(currentSize <= newCapacity);
+    shrinkCapacity(newCapacity);
+}
+
+template<typename T, size_t inlineCapacity, typename OverflowHandler, size_t minCapacity, typename Malloc>
 template<FailureAction action, typename U>
 ALWAYS_INLINE bool Vector<T, inlineCapacity, OverflowHandler, minCapacity, Malloc>::append(const U* data, size_t dataSize)
 {

Modified: trunk/Source/WTF/wtf/text/AtomStringImpl.cpp (280192 => 280193)


--- trunk/Source/WTF/wtf/text/AtomStringImpl.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WTF/wtf/text/AtomStringImpl.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -127,7 +127,10 @@
 
     static bool equal(PackedPtr<StringImpl> const& str, const UCharBuffer& buf)
     {
-        return WTF::equal(str.get(), buf.characters, buf.length);
+        StringImpl* impl = str.get();
+        if (impl->existingHash() != buf.hash)
+            return false;
+        return WTF::equal(impl, buf.characters, buf.length);
     }
 
     static void translate(PackedPtr<StringImpl>& location, const UCharBuffer& buf, unsigned hash)
@@ -307,7 +310,10 @@
 
     static bool equal(PackedPtr<StringImpl> const& str, const LCharBuffer& buf)
     {
-        return WTF::equal(str.get(), buf.characters, buf.length);
+        StringImpl* impl = str.get();
+        if (impl->existingHash() != buf.hash)
+            return false;
+        return WTF::equal(impl, buf.characters, buf.length);
     }
 
     static void translate(PackedPtr<StringImpl>& location, const LCharBuffer& buf, unsigned hash)
@@ -329,7 +335,10 @@
 
     static bool equal(PackedPtr<StringImpl> const& str, const Buffer& buf)
     {
-        return WTF::equal(str.get(), buf.characters, buf.length);
+        StringImpl* impl = str.get();
+        if (impl->existingHash() != buf.hash)
+            return false;
+        return WTF::equal(impl, buf.characters, buf.length);
     }
 
     static void translate(PackedPtr<StringImpl>& location, const Buffer& buf, unsigned hash)

Modified: trunk/Source/WTF/wtf/text/StringView.h (280192 => 280193)


--- trunk/Source/WTF/wtf/text/StringView.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WTF/wtf/text/StringView.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -131,6 +131,8 @@
     StringView right(unsigned length) const { return substring(this->length() - length, length); }
 
     template<typename MatchedCharacterPredicate>
+    StringView stripLeadingMatchedCharacters(const MatchedCharacterPredicate&);
+    template<typename MatchedCharacterPredicate>
     StringView stripLeadingAndTrailingMatchedCharacters(const MatchedCharacterPredicate&);
 
     class SplitResult;
@@ -182,6 +184,9 @@
     void initialize(const UChar*, unsigned length);
 
     template<typename CharacterType, typename MatchedCharacterPredicate>
+    StringView stripLeadingMatchedCharacters(const CharacterType*, const MatchedCharacterPredicate&);
+
+    template<typename CharacterType, typename MatchedCharacterPredicate>
     StringView stripLeadingAndTrailingMatchedCharacters(const CharacterType*, const MatchedCharacterPredicate&);
 
 #if CHECK_STRINGVIEW_LIFETIME
@@ -1030,6 +1035,32 @@
 }
 
 template<typename CharacterType, typename MatchedCharacterPredicate>
+inline StringView StringView::stripLeadingMatchedCharacters(const CharacterType* characters, const MatchedCharacterPredicate& predicate)
+{
+    unsigned start = 0;
+    while (start < m_length && predicate(characters[start]))
+        ++start;
+
+    if (start == m_length)
+        return StringView::empty();
+
+    if (!start)
+        return *this;
+
+    StringView result(characters + start, m_length - start);
+    result.setUnderlyingString(*this);
+    return result;
+}
+
+template<typename MatchedCharacterPredicate>
+StringView StringView::stripLeadingMatchedCharacters(const MatchedCharacterPredicate& predicate)
+{
+    if (is8Bit())
+        return stripLeadingMatchedCharacters<LChar>(characters8(), predicate);
+    return stripLeadingMatchedCharacters<UChar>(characters16(), predicate);
+}
+
+template<typename CharacterType, typename MatchedCharacterPredicate>
 inline StringView StringView::stripLeadingAndTrailingMatchedCharacters(const CharacterType* characters, const MatchedCharacterPredicate& predicate)
 {
     if (!m_length)

Modified: trunk/Source/WebCore/ChangeLog (280192 => 280193)


--- trunk/Source/WebCore/ChangeLog	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/ChangeLog	2021-07-22 19:18:45 UTC (rev 280193)
@@ -1,3 +1,75 @@
+2021-07-22  Yusuke Suzuki  <[email protected]>
+
+        Micro-optimize innerHTML
+        https://bugs.webkit.org/show_bug.cgi?id=228142
+
+        Reviewed by Simon Fraser.
+
+        No behavior change.
+
+        This patch does some micro optimizations revealed by the profiler when running some of Speedometer2 tests which intensively use innerHTML.
+        This offers improvement in jQuery-TodoMVC and Vanilla-ES2015-Babel-Webpack-TodoMVC since both are super innerHTML heavy benchmarks.
+
+        ----------------------------------------------------------------------------------------------------------------------------------
+        |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        | Elm-TodoMVC                          |126.862500   |126.687500   |0.998621  | 0.673462                                         |
+        | VueJS-TodoMVC                        |27.775000    |27.645833    |0.995350  | 0.741588                                         |
+        | EmberJS-TodoMVC                      |129.350000   |129.129167   |0.998293  | 0.624196                                         |
+        | BackboneJS-TodoMVC                   |51.129167    |51.204167    |1.001467  | 0.716622                                         |
+        | Preact-TodoMVC                       |21.870833    |21.337500    |0.975614  | 0.217771                                         |
+        | AngularJS-TodoMVC                    |139.854167   |140.266667   |1.002950  | 0.489838                                         |
+        | Vanilla-ES2015-TodoMVC               |69.229167    |68.895833    |0.995185  | 0.238772                                         |
+        | Inferno-TodoMVC                      |68.391667    |68.266667    |0.998172  | 0.762281                                         |
+        | Flight-TodoMVC                       |77.979167    |78.166667    |1.002404  | 0.710324                                         |
+        | Angular2-TypeScript-TodoMVC          |39.741667    |39.966667    |1.005662  | 0.524123                                         |
+        | VanillaJS-TodoMVC                    |55.416667    |55.512500    |1.001729  | 0.781447                                         |
+        | jQuery-TodoMVC                       |268.812500   |266.966667   |0.993133  | 0.003384 (significant)                           |
+        | EmberJS-Debug-TodoMVC                |345.383333   |345.662500   |1.000808  | 0.695259                                         |
+        | React-TodoMVC                        |90.679167    |90.179167    |0.994486  | 0.067477                                         |
+        | React-Redux-TodoMVC                  |152.691667   |152.687500   |0.999973  | 0.991207                                         |
+        | Vanilla-ES2015-Babel-Webpack-TodoMVC |66.487500    |65.729167    |0.988594  | 0.000118 (significant)                           |
+        ----------------------------------------------------------------------------------------------------------------------------------
+
+
+        a mean = 242.12319
+        b mean = 242.80485
+        pValue = 0.1992654128
+        (Bigger means are better.)
+        1.003 times better
+        Results ARE NOT significant
+
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::insertTextNode):
+        * html/parser/HTMLConstructionSite.h:
+        * html/parser/HTMLDocumentParser.cpp:
+        (WebCore::HTMLDocumentParser::pumpTokenizerLoop): We do not need to call `shrinkToBestFit` in fragment parsing case since
+        we will discard HTMLToken soon.
+        * html/parser/HTMLMetaCharsetParser.cpp:
+        (WebCore::HTMLMetaCharsetParser::checkForMetaCharset):
+        * html/parser/HTMLPreloadScanner.cpp:
+        (WebCore::HTMLPreloadScanner::scan):
+        * html/parser/HTMLToken.h:
+        (WebCore::HTMLToken::clear): We found that these `clear` calls cause performance problem according to the Instruments: we
+        repeatedly use this Vector, and we repeatedly allocate and deallocate this Vector unnecessarily. We use `resize(0)` instead
+        to avoid this allocation and deallocation.
+        (WebCore::HTMLToken::shrinkToBestFit): But HTMLToken is kept so long, so at some point, we would like to make backing storage
+        small. So, we add shrinkToBestFit and we call it only after finishing batching of HTMLToken processing.
+        (WebCore::HTMLToken::beginStartTag):
+        (WebCore::HTMLToken::beginEndTag):
+        * html/parser/HTMLTokenizer.h:
+        (WebCore::HTMLTokenizer::shrinkToBestFit):
+        * html/parser/HTMLTreeBuilder.cpp:
+        (WebCore::HTMLTreeBuilder::ExternalCharacterTokenBuffer::characterPredicate):
+        (WebCore::HTMLTreeBuilder::insertPhoneNumberLink):
+        (WebCore::HTMLTreeBuilder::linkifyPhoneNumbers):
+        (WebCore::HTMLTreeBuilder::processCharacterBuffer):
+        (WebCore::HTMLTreeBuilder::processCharacterBufferForInBody):
+        (WebCore::HTMLTreeBuilder::defaultForInTableText):
+        (WebCore::HTMLTreeBuilder::processTokenInForeignContent):
+        (WebCore::HTMLTreeBuilder::processFakeCharacters): Deleted. It is dead code before this patch.
+        * html/parser/HTMLTreeBuilder.h:
+
 2021-07-22  Wenson Hsieh  <[email protected]>
 
         Rename EventHandler::m_textRecognitionHoverTimerFired()

Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -566,7 +566,7 @@
         m_openElements.push(HTMLStackItem::create(WTFMove(element), WTFMove(token), namespaceURI));
 }
 
-void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode)
+void HTMLConstructionSite::insertTextNode(String&& characters, WhitespaceMode whitespaceMode)
 {
     HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
     task.parent = &currentNode();
@@ -596,7 +596,7 @@
         // If we have a whole string of unbreakable characters the above could lead to an infinite loop. Exceeding the length limit is the lesser evil.
         if (!textNode->length()) {
             String substring = characters.substring(currentPosition);
-            textNode = Text::create(task.parent->document(), shouldUseAtomString ? AtomString(substring).string() : substring);
+            textNode = Text::create(task.parent->document(), shouldUseAtomString ? AtomString(WTFMove(substring)).string() : substring);
         }
 
         currentPosition += textNode->length();

Modified: trunk/Source/WebCore/html/parser/HTMLConstructionSite.h (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLConstructionSite.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLConstructionSite.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -111,7 +111,7 @@
     void insertHTMLBodyElement(AtomicHTMLToken&&);
     void insertHTMLFormElement(AtomicHTMLToken&&, bool isDemoted = false);
     void insertScriptElement(AtomicHTMLToken&&);
-    void insertTextNode(const String&, WhitespaceMode = WhitespaceUnknown);
+    void insertTextNode(String&&, WhitespaceMode = WhitespaceUnknown);
     void insertForeignElement(AtomicHTMLToken&&, const AtomString& namespaceURI);
 
     void insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken&&);

Modified: trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -43,7 +43,7 @@
 #include "NavigationScheduler.h"
 #include "ScriptElement.h"
 #include "ThrowOnDynamicMarkupInsertionCountIncrementer.h"
-
+#include <wtf/Scope.h>
 #include <wtf/SystemTracing.h>
 
 namespace WebCore {
@@ -256,6 +256,11 @@
 
 bool HTMLDocumentParser::pumpTokenizerLoop(SynchronousMode mode, bool parsingFragment, PumpSession& session)
 {
+    auto scopeExit = makeScopeExit([&] {
+        if (!parsingFragment)
+            m_tokenizer.shrinkToBestFit();
+    });
+
     do {
         if (UNLIKELY(isWaitingForScripts())) {
             if (mode == AllowYield && m_parserScheduler->shouldYieldBeforeExecutingScript(m_treeBuilder->scriptToProcess(), session))

Modified: trunk/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -31,6 +31,7 @@
 #include "HTMLParserIdioms.h"
 #include "TextCodec.h"
 #include "TextEncodingRegistry.h"
+#include <wtf/Scope.h>
 
 namespace WebCore {
 
@@ -157,6 +158,10 @@
     bool ignoredSawErrorFlag;
     m_input.append(m_codec->decode(data, length, false, false, ignoredSawErrorFlag));
 
+    auto scopeExit = makeScopeExit([&] {
+        m_tokenizer.shrinkToBestFit();
+    });
+
     while (auto token = m_tokenizer.nextToken(m_input)) {
         bool isEnd = token->type() == HTMLToken::EndTag;
         if (isEnd || token->type() == HTMLToken::StartTag) {

Modified: trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLPreloadScanner.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -514,6 +514,7 @@
             m_tokenizer.updateStateFor(AtomString(token->name()));
         m_scanner.scan(*token, requests, document);
     }
+    m_tokenizer.shrinkToBestFit();
 
     preloader.preload(WTFMove(requests));
 }

Modified: trunk/Source/WebCore/html/parser/HTMLToken.h (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLToken.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLToken.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -68,6 +68,7 @@
     HTMLToken();
 
     void clear();
+    void shrinkToBestFit();
 
     Type type() const;
 
@@ -170,10 +171,16 @@
 inline void HTMLToken::clear()
 {
     m_type = Uninitialized;
-    m_data.clear();
+    m_data.resize(0);
     m_data8BitCheck = 0;
 }
 
+inline void HTMLToken::shrinkToBestFit()
+{
+    m_data.shrinkToBestFit();
+    m_attributes.shrinkToBestFit();
+}
+
 inline HTMLToken::Type HTMLToken::type() const
 {
     return m_type;
@@ -273,7 +280,7 @@
     ASSERT(m_type == Uninitialized);
     m_type = StartTag;
     m_selfClosing = false;
-    m_attributes.clear();
+    m_attributes.resize(0);
 
 #if ASSERT_ENABLED
     m_currentAttribute = nullptr;
@@ -288,7 +295,7 @@
     ASSERT(m_type == Uninitialized);
     m_type = EndTag;
     m_selfClosing = false;
-    m_attributes.clear();
+    m_attributes.resize(0);
 
 #if ASSERT_ENABLED
     m_currentAttribute = nullptr;
@@ -302,7 +309,7 @@
     ASSERT(m_type == Uninitialized);
     m_type = EndTag;
     m_selfClosing = false;
-    m_attributes.clear();
+    m_attributes.resize(0);
 
 #if ASSERT_ENABLED
     m_currentAttribute = nullptr;

Modified: trunk/Source/WebCore/html/parser/HTMLTokenizer.h (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLTokenizer.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLTokenizer.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -79,6 +79,11 @@
 
     bool neverSkipNullCharacters() const;
 
+    void shrinkToBestFit()
+    {
+        m_token.shrinkToBestFit();
+    }
+
 private:
     enum State {
         DataState,

Modified: trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -226,11 +226,7 @@
     template<bool characterPredicate(UChar)> void skipLeading()
     {
         ASSERT(!isEmpty());
-        while (characterPredicate(m_text[0])) {
-            m_text = m_text.substring(1);
-            if (m_text.isEmpty())
-                return;
-        }
+        m_text = m_text.stripLeadingMatchedCharacters(characterPredicate);
     }
 
     template<bool characterPredicate(UChar)> String takeLeading()
@@ -433,13 +429,6 @@
     processFakeEndTag(tagName.localName());
 }
 
-void HTMLTreeBuilder::processFakeCharacters(const String& characters)
-{
-    ASSERT(!characters.isEmpty());
-    ExternalCharacterTokenBuffer buffer(characters);
-    processCharacterBuffer(buffer);
-}
-
 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
 {
     if (!m_tree.openElements().inButtonScope(pTag->localName()))
@@ -2195,7 +2184,7 @@
 
 // FIXME: Extract the following iOS-specific code into a separate file.
 // From the string 4089961010, creates a link of the form <a href="" and inserts it.
-void HTMLTreeBuilder::insertPhoneNumberLink(const String& string)
+void HTMLTreeBuilder::insertPhoneNumberLink(String&& string)
 {
     Vector<Attribute> attributes;
     attributes.append(Attribute(HTMLNames::hrefAttr, makeString("tel:"_s, string)));
@@ -2206,7 +2195,7 @@
 
     processStartTag(WTFMove(aStartToken));
     m_tree.executeQueuedTasks();
-    m_tree.insertTextNode(string);
+    m_tree.insertTextNode(WTFMove(string));
     processEndTag(WTFMove(aEndToken));
 }
 
@@ -2215,7 +2204,7 @@
 // 2. Wraps the phone number in a tel: link.
 // 3. Goes back to step 1 if a phone number is found in the rest of the string.
 // 4. Appends the rest of the string as a text node.
-void HTMLTreeBuilder::linkifyPhoneNumbers(const String& string)
+void HTMLTreeBuilder::linkifyPhoneNumbers(String&& string)
 {
     ASSERT(TelephoneNumberDetector::isSupported());
 
@@ -2248,10 +2237,10 @@
     if (scannerPosition > 0) {
         if (scannerPosition < length) {
             String after = string.substring(scannerPosition, length - scannerPosition);
-            m_tree.insertTextNode(after);
+            m_tree.insertTextNode(WTFMove(after));
         }
     } else
-        m_tree.insertTextNode(string);
+        m_tree.insertTextNode(WTFMove(string));
 }
 
 // Looks at the ancestors of the element to determine whether we're inside an element which disallows parsing phone numbers.
@@ -2324,7 +2313,7 @@
     case InsertionMode::InHead: {
         String leadingWhitespace = buffer.takeLeadingWhitespace();
         if (!leadingWhitespace.isEmpty())
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         if (buffer.isEmpty())
             return;
         defaultForInHead();
@@ -2334,7 +2323,7 @@
     case InsertionMode::AfterHead: {
         String leadingWhitespace = buffer.takeLeadingWhitespace();
         if (!leadingWhitespace.isEmpty())
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         if (buffer.isEmpty())
             return;
         defaultForAfterHead();
@@ -2372,7 +2361,7 @@
     case InsertionMode::InColumnGroup: {
         String leadingWhitespace = buffer.takeLeadingWhitespace();
         if (!leadingWhitespace.isEmpty())
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         if (buffer.isEmpty())
             return;
         if (!processColgroupEndTagForInColumnGroup()) {
@@ -2395,7 +2384,7 @@
     case InsertionMode::InHeadNoscript: {
         String leadingWhitespace = buffer.takeLeadingWhitespace();
         if (!leadingWhitespace.isEmpty())
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         if (buffer.isEmpty())
             return;
         defaultForInHeadNoscript();
@@ -2405,7 +2394,7 @@
     case InsertionMode::AfterFrameset: {
         String leadingWhitespace = buffer.takeRemainingWhitespace();
         if (!leadingWhitespace.isEmpty())
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         // FIXME: We should generate a parse error if we skipped over any
         // non-whitespace characters.
         break;
@@ -2418,7 +2407,7 @@
         String leadingWhitespace = buffer.takeRemainingWhitespace();
         if (!leadingWhitespace.isEmpty()) {
             m_tree.reconstructTheActiveFormattingElements();
-            m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
+            m_tree.insertTextNode(WTFMove(leadingWhitespace), AllWhitespace);
         }
         // FIXME: We should generate a parse error if we skipped over any
         // non-whitespace characters.
@@ -2431,16 +2420,16 @@
 {
     m_tree.reconstructTheActiveFormattingElements();
     String characters = buffer.takeRemaining();
+    if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
+        m_framesetOk = false;
 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS_FAMILY)
     if (!isParsingFragment() && m_tree.isTelephoneNumberParsingEnabled() && shouldParseTelephoneNumbersInNode(m_tree.currentNode()) && TelephoneNumberDetector::isSupported())
-        linkifyPhoneNumbers(characters);
+        linkifyPhoneNumbers(WTFMove(characters));
     else
-        m_tree.insertTextNode(characters);
+        m_tree.insertTextNode(WTFMove(characters));
 #else
-    m_tree.insertTextNode(characters);
+    m_tree.insertTextNode(WTFMove(characters));
 #endif
-    if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
-        m_framesetOk = false;
 }
 
 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken&& token)
@@ -2577,12 +2566,12 @@
         // FIXME: parse error
         HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
         m_tree.reconstructTheActiveFormattingElements();
-        m_tree.insertTextNode(characters, NotAllWhitespace);
+        m_tree.insertTextNode(WTFMove(characters), NotAllWhitespace);
         m_framesetOk = false;
         m_insertionMode = m_originalInsertionMode;
         return;
     }
-    m_tree.insertTextNode(characters);
+    m_tree.insertTextNode(WTFMove(characters));
     m_insertionMode = m_originalInsertionMode;
 }
 
@@ -2820,9 +2809,9 @@
         return;
     case HTMLToken::Character: {
         String characters = String(token.characters(), token.charactersLength());
-        m_tree.insertTextNode(characters);
         if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
             m_framesetOk = false;
+        m_tree.insertTextNode(WTFMove(characters));
         break;
     }
     case HTMLToken::EndOfFile:

Modified: trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h (280192 => 280193)


--- trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h	2021-07-22 19:18:45 UTC (rev 280193)
@@ -108,8 +108,8 @@
     bool isParsingFragmentOrTemplateContents() const;
 
 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS_FAMILY)
-    void insertPhoneNumberLink(const String&);
-    void linkifyPhoneNumbers(const String&);
+    void insertPhoneNumberLink(String&&);
+    void linkifyPhoneNumbers(String&&);
 #endif
 
     void processToken(AtomicHTMLToken&&);
@@ -145,7 +145,6 @@
     void processFakeStartTag(const QualifiedName&, Vector<Attribute>&& attributes = Vector<Attribute>());
     void processFakeEndTag(const QualifiedName&);
     void processFakeEndTag(const AtomString&);
-    void processFakeCharacters(const String&);
     void processFakePEndTagIfPInButtonScope();
 
     void processGenericRCDATAStartTag(AtomicHTMLToken&&);

Modified: trunk/Tools/ChangeLog (280192 => 280193)


--- trunk/Tools/ChangeLog	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Tools/ChangeLog	2021-07-22 19:18:45 UTC (rev 280193)
@@ -1,3 +1,15 @@
+2021-07-22  Yusuke Suzuki  <[email protected]>
+
+        Micro-optimize innerHTML
+        https://bugs.webkit.org/show_bug.cgi?id=228142
+
+        Reviewed by Simon Fraser.
+
+        * TestWebKitAPI/Tests/WTF/StringView.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WTF/Vector.cpp:
+        (TestWebKitAPI::TEST):
+
 2021-07-22  Peng Liu  <[email protected]>
 
         [ BigSur Debug ] TestWebKitAPI.GPUProcess.CrashWhilePlayingAudioViaCreateMediaElementSource is flaky, hitting ASSERTION FAILED: !isInRoutingArbitrationForToken(token)

Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp (280192 => 280193)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/StringView.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -959,6 +959,28 @@
     EXPECT_EQ(reference.reverseFind('c', 4), notFound);
 }
 
+TEST(WTF, StringViewStripLeadingMatchedCharacters)
+{
+    auto isA = [] (UChar c) {
+        return c == 'A';
+    };
+
+    EXPECT_TRUE(stringViewFromLiteral("AAABBBAAA").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("BBBAAA"));
+    EXPECT_TRUE(stringViewFromLiteral("AAABBBCCC").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("BBBCCC"));
+    EXPECT_TRUE(stringViewFromLiteral("CCCBBBAAA").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("CCCBBBAAA"));
+    EXPECT_TRUE(stringViewFromLiteral("CCCBBBCCC").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("CCCBBBCCC"));
+    EXPECT_TRUE(stringViewFromLiteral("AAAAAACCC").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("CCC"));
+    EXPECT_TRUE(stringViewFromLiteral("BBBAAAAAA").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("BBBAAAAAA"));
+    EXPECT_TRUE(stringViewFromLiteral("CCCAAABBB").stripLeadingMatchedCharacters(isA) == stringViewFromLiteral("CCCAAABBB"));
+    EXPECT_TRUE(stringViewFromLiteral("AAAAAAAAA").stripLeadingMatchedCharacters(isA) == StringView::empty());
+
+    StringView emptyView = StringView::empty();
+    EXPECT_TRUE(emptyView.stripLeadingMatchedCharacters(isA) == emptyView);
+
+    StringView nullView;
+    EXPECT_TRUE(nullView.stripLeadingMatchedCharacters(isA) == nullView);
+}
+
 TEST(WTF, StringViewStripLeadingAndTrailingMatchedCharacters)
 {
     auto isA = [] (UChar c) { 

Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp (280192 => 280193)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp	2021-07-22 19:13:01 UTC (rev 280192)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp	2021-07-22 19:18:45 UTC (rev 280193)
@@ -1458,5 +1458,41 @@
     EXPECT_EQ(vector[3], 4);
     EXPECT_EQ(vector[4], 5);
 }
-    
+
+TEST(WTF_Vector, ShrinkToBestFitClear)
+{
+    Vector<uint32_t> vector;
+    vector.append(20);
+    vector.append(20);
+    vector.append(20);
+    vector.resize(0);
+    vector.shrinkToBestFit();
+    EXPECT_EQ(vector.size(), 0U);
+    EXPECT_EQ(vector.capacity(), 0U);
+}
+
+TEST(WTF_Vector, ShrinkToBestFitDoesNotChangeIfCapacityMatches)
+{
+    Vector<uint32_t> vector;
+    vector.append(20);
+    vector.append(20);
+    vector.append(20);
+    unsigned capacity = vector.capacity();
+    vector.shrinkToBestFit();
+    EXPECT_EQ(vector.size(), 3U);
+    EXPECT_EQ(vector.capacity(), capacity);
+}
+
+TEST(WTF_Vector, ShrinkToBestFit)
+{
+    Vector<uint32_t> vector;
+    for (unsigned index = 0; index < 400; ++index)
+        vector.append(index);
+    vector.resize(100);
+    EXPECT_GE(vector.capacity(), 400U);
+    vector.shrinkToBestFit();
+    EXPECT_LE(vector.capacity(), 200U);
+    EXPECT_GE(vector.capacity(), 100U);
+}
+
 } // namespace TestWebKitAPI
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to