<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[282142] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/282142">282142</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2021-09-08 07:01:50 -0700 (Wed, 08 Sep 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add a fast path for atomizing strings when parsing HTML
https://bugs.webkit.org/show_bug.cgi?id=229907
rdar://82854612

Reviewed by Yusuke Suzuki and Darin Adler.

On various subtests in Speedometer 2, a nontrivial amount of time is spent mapping raw UChar data vectors into
AtomStrings while parsing HTML tag names, attribute names and attribute values. Most of this happens underneath
the AtomHTMLToken constructor, which computes a hash for each string in the process of adding it to the atom
string table; the time it takes to compute this string hash increases linearly with the length of the string.

However, over the course of the benchmark, the vast majority of AtomStrings created out of tag names, attribute
names and attribute values are both:

(1) Strings that we've already recently atomized, and
(2) Usually distinguishable from other atom strings based solely on their first character, last character, and
    overall string length.

As such, it's possible to slightly improve string atomization performance in this particular case (i.e. parsing
HTML) by maintaining a smaller cache of recently atomized AtomStrings that we index using a simple, constant-
time hash function that considers only the first character, last character, and length of the string. In terms
of the cache hit rate in this AtomString cache, the default string hashing algorithm only barely outperforms
this simple hash function on Speedometer (i.e., a cache hit rate of 99.24% using the default hash algorithm vs.
99.15% using the "first/last character and length" hash).

Using this technique, we can get a significant performance improvement on Speedometer by introducing two small,
fixed-size (512 capacity) AtomString tables: one to hold tag names and attribute names, and another to hold
attribute values (which seems to contain a much larger set of unique strings); we additionally use the cheap "2-
char & length" hash algorithm described above to index into these fixed-size tables.

This allows us to more efficiently atomize not only known tag and attribute names, but also custom element tag
names and attribute names and values that tend to appear frequently in markup (e.g. due to using certain
JavaScript frameworks that get and set HTML attributes).

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* html/parser/AtomHTMLToken.h:
(WebCore::AtomHTMLToken::initializeAttributes):
(WebCore::AtomHTMLToken::AtomHTMLToken):
* html/parser/HTMLAtomStringCache.cpp: Added.
(WebCore::HTMLAtomStringCache::cache):
* html/parser/HTMLAtomStringCache.h: Added.

Add a helper class that exposes three static inline helper methods: `makeTagOrAttributeName` and
`makeAttributeValue`, which return AtomStrings for the given `Vector<UChar>` (consulting the corresponding
cache if possible); and `clear`, which empties all cached atom strings.

(WebCore::HTMLAtomStringCache::makeTagOrAttributeName):
(WebCore::HTMLAtomStringCache::makeAttributeValue):
(WebCore::HTMLAtomStringCache::clear):
(WebCore::HTMLAtomStringCache::make):

Additionally add an upper length limit for characters that we include in this cache; in practice, longer strings
tend to be repeatedly atomized less frequently than shorter strings. The 36-character limit also allows for
frequently-parsed (and atomized) UUIDs to be cached.

(WebCore::HTMLAtomStringCache::cacheSlot):

This hashing algorithm was inspired by `calculateWithTwoCharacters`, but with constants specifically chosen to
minimize collisions between common HTML tag and attribute names.

* page/MemoryRelease.cpp:
(WebCore::releaseNoncriticalMemory):
* page/cocoa/MemoryReleaseCocoa.mm:
(WebCore::jettisonExpensiveObjectsOnTopLevelNavigation):

Add logic to clear the HTML atom string cache upon receiving a low memory warning, and upon top-level
navigation.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreSourcestxt">trunk/Source/WebCore/Sources.txt</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCorehtmlparserAtomHTMLTokenh">trunk/Source/WebCore/html/parser/AtomHTMLToken.h</a></li>
<li><a href="#trunkSourceWebCorepageMemoryReleasecpp">trunk/Source/WebCore/page/MemoryRelease.cpp</a></li>
<li><a href="#trunkSourceWebCorepagecocoaMemoryReleaseCocoamm">trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCorehtmlparserHTMLAtomStringCachecpp">trunk/Source/WebCore/html/parser/HTMLAtomStringCache.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlparserHTMLAtomStringCacheh">trunk/Source/WebCore/html/parser/HTMLAtomStringCache.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/ChangeLog      2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -1,3 +1,74 @@
</span><ins>+2021-09-08  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add a fast path for atomizing strings when parsing HTML
+        https://bugs.webkit.org/show_bug.cgi?id=229907
+        rdar://82854612
+
+        Reviewed by Yusuke Suzuki and Darin Adler.
+
+        On various subtests in Speedometer 2, a nontrivial amount of time is spent mapping raw UChar data vectors into
+        AtomStrings while parsing HTML tag names, attribute names and attribute values. Most of this happens underneath
+        the AtomHTMLToken constructor, which computes a hash for each string in the process of adding it to the atom
+        string table; the time it takes to compute this string hash increases linearly with the length of the string.
+
+        However, over the course of the benchmark, the vast majority of AtomStrings created out of tag names, attribute
+        names and attribute values are both:
+
+        (1) Strings that we've already recently atomized, and
+        (2) Usually distinguishable from other atom strings based solely on their first character, last character, and
+            overall string length.
+
+        As such, it's possible to slightly improve string atomization performance in this particular case (i.e. parsing
+        HTML) by maintaining a smaller cache of recently atomized AtomStrings that we index using a simple, constant-
+        time hash function that considers only the first character, last character, and length of the string. In terms
+        of the cache hit rate in this AtomString cache, the default string hashing algorithm only barely outperforms
+        this simple hash function on Speedometer (i.e., a cache hit rate of 99.24% using the default hash algorithm vs.
+        99.15% using the "first/last character and length" hash).
+
+        Using this technique, we can get a significant performance improvement on Speedometer by introducing two small,
+        fixed-size (512 capacity) AtomString tables: one to hold tag names and attribute names, and another to hold
+        attribute values (which seems to contain a much larger set of unique strings); we additionally use the cheap "2-
+        char & length" hash algorithm described above to index into these fixed-size tables.
+
+        This allows us to more efficiently atomize not only known tag and attribute names, but also custom element tag
+        names and attribute names and values that tend to appear frequently in markup (e.g. due to using certain
+        JavaScript frameworks that get and set HTML attributes).
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * html/parser/AtomHTMLToken.h:
+        (WebCore::AtomHTMLToken::initializeAttributes):
+        (WebCore::AtomHTMLToken::AtomHTMLToken):
+        * html/parser/HTMLAtomStringCache.cpp: Added.
+        (WebCore::HTMLAtomStringCache::cache):
+        * html/parser/HTMLAtomStringCache.h: Added.
+
+        Add a helper class that exposes three static inline helper methods: `makeTagOrAttributeName` and
+        `makeAttributeValue`, which return AtomStrings for the given `Vector<UChar>` (consulting the corresponding
+        cache if possible); and `clear`, which empties all cached atom strings.
+
+        (WebCore::HTMLAtomStringCache::makeTagOrAttributeName):
+        (WebCore::HTMLAtomStringCache::makeAttributeValue):
+        (WebCore::HTMLAtomStringCache::clear):
+        (WebCore::HTMLAtomStringCache::make):
+
+        Additionally add an upper length limit for characters that we include in this cache; in practice, longer strings
+        tend to be repeatedly atomized less frequently than shorter strings. The 36-character limit also allows for
+        frequently-parsed (and atomized) UUIDs to be cached.
+
+        (WebCore::HTMLAtomStringCache::cacheSlot):
+
+        This hashing algorithm was inspired by `calculateWithTwoCharacters`, but with constants specifically chosen to
+        minimize collisions between common HTML tag and attribute names.
+
+        * page/MemoryRelease.cpp:
+        (WebCore::releaseNoncriticalMemory):
+        * page/cocoa/MemoryReleaseCocoa.mm:
+        (WebCore::jettisonExpensiveObjectsOnTopLevelNavigation):
+
+        Add logic to clear the HTML atom string cache upon receiving a low memory warning, and upon top-level
+        navigation.
+
</ins><span class="cx"> 2021-09-08  Alan Bujtas  <zalan@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [LFC][IFC] Add support for inline box ink overflow
</span></span></pre></div>
<a id="trunkSourceWebCoreSourcestxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Sources.txt (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Sources.txt 2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/Sources.txt    2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -1312,6 +1312,7 @@
</span><span class="cx"> html/canvas/WebGLVertexArrayObjectOES.cpp
</span><span class="cx"> html/forms/FileIconLoader.cpp
</span><span class="cx"> html/parser/CSSPreloadScanner.cpp
</span><ins>+html/parser/HTMLAtomStringCache.cpp
</ins><span class="cx"> html/parser/HTMLConstructionSite.cpp
</span><span class="cx"> html/parser/HTMLDocumentParser.cpp
</span><span class="cx"> html/parser/HTMLElementStack.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj   2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj      2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -5409,6 +5409,7 @@
</span><span class="cx">          F48D2A7E2157182600C6752B /* FontAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2A712156DC0A00C6752B /* FontAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">          F48D2AA52159740D00C6752B /* ColorCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2AA32159740D00C6752B /* ColorCocoa.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">          F49786881FF45FA500E060AB /* PasteboardItemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F49786871FF45FA500E060AB /* PasteboardItemInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+               F4B0018926E7F21F006EAABE /* HTMLAtomStringCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B0018726E7F21F006EAABE /* HTMLAtomStringCache.h */; };
</ins><span class="cx">           F4B2A909265030BA009E7286 /* DataDetectorHighlight.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B2A90626502BA0009E7286 /* DataDetectorHighlight.h */; };
</span><span class="cx">          F4B422C4220C0568009E1E7D /* DOMPasteAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">          F4BFB9851E1DDF9B00862C24 /* DumpEditingHistory.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389831E1DDF2B0076B7EA /* DumpEditingHistory.js */; };
</span><span class="lines">@@ -16640,6 +16641,8 @@
</span><span class="cx">          F48D2AA42159740D00C6752B /* ColorCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorCocoa.mm; sourceTree = "<group>"; };
</span><span class="cx">          F49786871FF45FA500E060AB /* PasteboardItemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasteboardItemInfo.h; sourceTree = "<group>"; };
</span><span class="cx">          F49E98E421DEE6C1009AE55E /* EditAction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EditAction.cpp; sourceTree = "<group>"; };
</span><ins>+               F4B0018726E7F21F006EAABE /* HTMLAtomStringCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HTMLAtomStringCache.h; sourceTree = "<group>"; };
+               F4B0018826E7F21F006EAABE /* HTMLAtomStringCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLAtomStringCache.cpp; sourceTree = "<group>"; };
</ins><span class="cx">           F4B2A90626502BA0009E7286 /* DataDetectorHighlight.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DataDetectorHighlight.h; sourceTree = "<group>"; };
</span><span class="cx">          F4B2A90826502BC0009E7286 /* DataDetectorHighlight.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DataDetectorHighlight.mm; sourceTree = "<group>"; };
</span><span class="cx">          F4B2A90C265087E4009E7286 /* ImageOverlayControllerMac.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ImageOverlayControllerMac.mm; sourceTree = "<group>"; };
</span><span class="lines">@@ -24192,6 +24195,8 @@
</span><span class="cx">                          97C1F552122855CB00EDE617 /* AtomHTMLToken.h */,
</span><span class="cx">                          977B3849122883E900B81FF8 /* CSSPreloadScanner.cpp */,
</span><span class="cx">                          977B384A122883E900B81FF8 /* CSSPreloadScanner.h */,
</span><ins>+                               F4B0018826E7F21F006EAABE /* HTMLAtomStringCache.cpp */,
+                               F4B0018726E7F21F006EAABE /* HTMLAtomStringCache.h */,
</ins><span class="cx">                           977B384B122883E900B81FF8 /* HTMLConstructionSite.cpp */,
</span><span class="cx">                          977B384C122883E900B81FF8 /* HTMLConstructionSite.h */,
</span><span class="cx">                          977B384D122883E900B81FF8 /* HTMLDocumentParser.cpp */,
</span><span class="lines">@@ -32306,6 +32311,7 @@
</span><span class="cx">                          BC97E23A109144950010D361 /* HTMLAllCollection.h in Headers */,
</span><span class="cx">                          A8CFF7AB0A156978000A4234 /* HTMLAnchorElement.h in Headers */,
</span><span class="cx">                          A8EA7D2E0A19385500A8EF5F /* HTMLAreaElement.h in Headers */,
</span><ins>+                               F4B0018926E7F21F006EAABE /* HTMLAtomStringCache.h in Headers */,
</ins><span class="cx">                           7C5F28FC1A827D8400C0F31F /* HTMLAttachmentElement.h in Headers */,
</span><span class="cx">                          E44613A20CD6331000FADA75 /* HTMLAudioElement.h in Headers */,
</span><span class="cx">                          A871DC1F0A15205700B12A68 /* HTMLBaseElement.h in Headers */,
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlparserAtomHTMLTokenh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/parser/AtomHTMLToken.h (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/parser/AtomHTMLToken.h 2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/html/parser/AtomHTMLToken.h    2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><ins>+#include "HTMLAtomStringCache.h"
</ins><span class="cx"> #include "HTMLToken.h"
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -202,11 +203,11 @@
</span><span class="cx">         if (attribute.name.isEmpty())
</span><span class="cx">             continue;
</span><span class="cx"> 
</span><del>-        AtomString localName(attribute.name);
</del><ins>+        auto localName = HTMLAtomStringCache::makeTagOrAttributeName(attribute.name);
</ins><span class="cx"> 
</span><span class="cx">         // FIXME: This is N^2 for the number of attributes.
</span><span class="cx">         if (!hasAttribute(m_attributes, localName))
</span><del>-            m_attributes.uncheckedAppend(Attribute(QualifiedName(nullAtom(), localName, nullAtom()), AtomString(attribute.value)));
</del><ins>+            m_attributes.uncheckedAppend(Attribute(QualifiedName(nullAtom(), localName, nullAtom()), HTMLAtomStringCache::makeAttributeValue(attribute.value)));
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -218,7 +219,7 @@
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         return;
</span><span class="cx">     case HTMLToken::DOCTYPE:
</span><del>-        m_name = AtomString(token.name());
</del><ins>+        m_name = HTMLAtomStringCache::makeTagOrAttributeName(token.name());
</ins><span class="cx">         m_doctypeData = token.releaseDoctypeData();
</span><span class="cx">         return;
</span><span class="cx">     case HTMLToken::EndOfFile:
</span><span class="lines">@@ -226,7 +227,7 @@
</span><span class="cx">     case HTMLToken::StartTag:
</span><span class="cx">     case HTMLToken::EndTag:
</span><span class="cx">         m_selfClosing = token.selfClosing();
</span><del>-        m_name = AtomString(token.name());
</del><ins>+        m_name = HTMLAtomStringCache::makeTagOrAttributeName(token.name());
</ins><span class="cx">         initializeAttributes(token.attributes());
</span><span class="cx">         return;
</span><span class="cx">     case HTMLToken::Comment:
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlparserHTMLAtomStringCachecpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/html/parser/HTMLAtomStringCache.cpp (0 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/parser/HTMLAtomStringCache.cpp                         (rev 0)
+++ trunk/Source/WebCore/html/parser/HTMLAtomStringCache.cpp    2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HTMLAtomStringCache.h"
+
+namespace WebCore {
+
+HTMLAtomStringCache::Cache& HTMLAtomStringCache::cache(Type type)
+{
+    static MainThreadNeverDestroyed<Cache> caches[2];
+    return caches[static_cast<size_t>(type)].get();
+}
+
+} // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCorehtmlparserHTMLAtomStringCacheh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/html/parser/HTMLAtomStringCache.h (0 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/parser/HTMLAtomStringCache.h                           (rev 0)
+++ trunk/Source/WebCore/html/parser/HTMLAtomStringCache.h      2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -0,0 +1,94 @@
</span><ins>+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomString.h>
+
+namespace WebCore {
+
+class HTMLAtomStringCache {
+public:
+    template<size_t inlineCapacity>
+    ALWAYS_INLINE static AtomString makeTagOrAttributeName(const Vector<UChar, inlineCapacity>& string)
+    {
+        return make<Type::TagOrAttributeName>(string);
+    }
+
+    template<size_t inlineCapacity>
+    ALWAYS_INLINE static AtomString makeAttributeValue(const Vector<UChar, inlineCapacity>& string)
+    {
+        return make<Type::AttributeValue>(string);
+    }
+
+    ALWAYS_INLINE static void clear()
+    {
+        // FIXME (webkit.org/b/230019): We should try to find more opportunities to clear this cache without hindering this performance optimization.
+        cache(Type::TagOrAttributeName).fill({ });
+        cache(Type::AttributeValue).fill({ });
+    }
+
+private:
+    enum class Type : bool { TagOrAttributeName, AttributeValue };
+
+    template<HTMLAtomStringCache::Type type, size_t inlineCapacity>
+    ALWAYS_INLINE static AtomString make(const Vector<UChar, inlineCapacity>& string)
+    {
+        if (string.isEmpty())
+            return emptyAtom();
+
+        auto length = string.size();
+        if (length > maxStringLengthForCache)
+            return AtomString(string);
+
+        auto firstCharacter = string[0];
+        auto lastCharacter = string[length - 1];
+        auto& slot = cacheSlot(type, firstCharacter, lastCharacter, length);
+        if (!equal(slot.impl(), string.data(), length)) {
+            AtomString result(string);
+            slot = result;
+            return result;
+        }
+
+        return slot;
+    }
+
+    ALWAYS_INLINE static AtomString& cacheSlot(Type type, UChar firstCharacter, UChar lastCharacter, UChar length)
+    {
+        unsigned hash = (firstCharacter << 6) ^ ((lastCharacter << 14) ^ firstCharacter);
+        hash += (hash >> 14) + (length << 14);
+        hash ^= hash << 14;
+        return cache(type)[(hash + (hash >> 6)) % capacity];
+    }
+
+    static constexpr auto maxStringLengthForCache = 36;
+    static constexpr auto capacity = 512;
+    using Cache = std::array<AtomString, capacity>;
+    static Cache& cache(Type);
+};
+
+} // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCorepageMemoryReleasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/MemoryRelease.cpp (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/MemoryRelease.cpp      2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/page/MemoryRelease.cpp 2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #include "FontCache.h"
</span><span class="cx"> #include "Frame.h"
</span><span class="cx"> #include "GCController.h"
</span><ins>+#include "HTMLAtomStringCache.h"
</ins><span class="cx"> #include "HTMLMediaElement.h"
</span><span class="cx"> #include "InlineStyleSheetOwner.h"
</span><span class="cx"> #include "InspectorInstrumentation.h"
</span><span class="lines">@@ -85,6 +86,7 @@
</span><span class="cx">         MemoryCache::singleton().pruneDeadResourcesToSize(0);
</span><span class="cx"> 
</span><span class="cx">     InlineStyleSheetOwner::clearCache();
</span><ins>+    HTMLAtomStringCache::clear();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void releaseCriticalMemory(Synchronous synchronous, MaintainBackForwardCache maintainBackForwardCache, MaintainMemoryCache maintainMemoryCache)
</span></span></pre></div>
<a id="trunkSourceWebCorepagecocoaMemoryReleaseCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm (282141 => 282142)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm    2021-09-08 13:33:30 UTC (rev 282141)
+++ trunk/Source/WebCore/page/cocoa/MemoryReleaseCocoa.mm       2021-09-08 14:01:50 UTC (rev 282142)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> #import "FontFamilySpecificationCoreText.h"
</span><span class="cx"> #import "GCController.h"
</span><ins>+#import "HTMLAtomStringCache.h"
</ins><span class="cx"> #import "IOSurfacePool.h"
</span><span class="cx"> #import "LayerPool.h"
</span><span class="cx"> #import "LocaleCocoa.h"
</span><span class="lines">@@ -83,7 +84,6 @@
</span><span class="cx"> 
</span><span class="cx"> void jettisonExpensiveObjectsOnTopLevelNavigation()
</span><span class="cx"> {
</span><del>-#if PLATFORM(IOS_FAMILY)
</del><span class="cx">     // Protect against doing excessive jettisoning during repeated navigations.
</span><span class="cx">     const auto minimumTimeSinceNavigation = 2_s;
</span><span class="cx"> 
</span><span class="lines">@@ -95,10 +95,13 @@
</span><span class="cx">     if (!shouldJettison)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><ins>+#if PLATFORM(IOS_FAMILY)
</ins><span class="cx">     // Throw away linked JS code. Linked code is tied to a global object and is not reusable.
</span><span class="cx">     // The immediate memory savings outweigh the cost of recompilation in case we go back again.
</span><span class="cx">     GCController::singleton().deleteAllLinkedCode(JSC::DeleteAllCodeIfNotCollecting);
</span><span class="cx"> #endif
</span><ins>+
+    HTMLAtomStringCache::clear();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void registerMemoryReleaseNotifyCallbacks()
</span></span></pre>
</div>
</div>

</body>
</html>