Changeset 285955 in webkit
- Timestamp:
- Nov 17, 2021 2:51:04 PM (8 months ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 2 added
- 7 edited
-
CMakeLists.txt (modified) (1 diff)
-
ChangeLog (modified) (1 diff)
-
JavaScriptCore.xcodeproj/project.pbxproj (modified) (4 diffs)
-
heap/Heap.cpp (modified) (1 diff)
-
runtime/JSONAtomStringCache.h (added)
-
runtime/JSONAtomStringCacheInlines.h (added)
-
runtime/LiteralParser.cpp (modified) (5 diffs)
-
runtime/LiteralParser.h (modified) (2 diffs)
-
runtime/VM.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/CMakeLists.txt
r285795 r285955 1086 1086 runtime/JSModuleRecord.h 1087 1087 runtime/JSNativeStdFunction.h 1088 runtime/JSONAtomStringCache.h 1088 1089 runtime/JSONObject.h 1089 1090 runtime/JSObject.h -
trunk/Source/JavaScriptCore/ChangeLog
r285850 r285955 1 2021-11-17 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Revise JSON.parse atomize policy 4 https://bugs.webkit.org/show_bug.cgi?id=233231 5 6 Reviewed by Mark Lam. 7 8 This patch improves JSON.parse performance by the following two changes. 9 10 1. Introduce JSONAtomStringCache. It is inspired from HTMLAtomStringCache. It offers cheap 11 fixed-sized cache stored in VM. Since it is in VM, we do not need to clear it every time 12 we call JSON.parse. We clear this cache when full GC happens. It contributes to 13 flight-todomvc-json-parse by 5%. 14 2. Do not atomize long string. Profiling of JSON.parse said that most of time is used for 15 atomizing of Strings. There is a tradeoff that, atomizing strings can reduce duplicate string 16 allocations, but it has a performance penalty. V8 limits atomizing for <= 10 length strings, 17 and SpiderMonkey does not atomize strings. In this patch, we aligned our atomizing policy to 18 V8, so we do not atomize strings if the length is longer than 10. It contributes to 19 flight-todomvc-json-parse by 50%. 20 21 Many microbenchmarks show the improvement. 22 ToT Patched 23 24 json-parse-object-reviver-same-value 78.2683+-0.9598 77.7784+-0.9488 25 vanilla-es2015-babel-webpack-todomvc-json-parse 26 99.9129+-0.5508 ^ 85.8160+-0.8721 ^ definitely 1.1643x faster 27 json-parse-array-reviver-same-value 63.5891+-0.8066 63.2895+-0.7336 28 flight-todomvc-json-parse 52.4230+-0.4474 ^ 34.1159+-0.2378 ^ definitely 1.5366x faster 29 json-parse-object-reviver 80.8417+-0.5042 80.6393+-0.8087 30 json-parse-leaf-object 51.6836+-0.6754 ^ 46.8983+-0.1578 ^ definitely 1.1020x faster 31 vanilla-es2015-todomvc-json-parse 100.5916+-0.9399 ^ 85.9522+-0.8470 ^ definitely 1.1703x faster 32 vanilla-todomvc-json-parse 76.4518+-0.4341 ^ 64.2318+-0.7621 ^ definitely 1.1902x faster 33 json-parse-array-reviver 76.1276+-0.8529 75.9747+-0.9002 34 35 And Speedometer2 shows 0.8% improvement. 36 37 ---------------------------------------------------------------------------------------------------------------------------------- 38 | subtest | ms | ms | b / a | pValue (significance using False Discovery Rate) | 39 ---------------------------------------------------------------------------------------------------------------------------------- 40 | Elm-TodoMVC |109.046667 |108.546667 |0.995415 | 0.197186 | 41 | VueJS-TodoMVC |21.813333 |21.566667 |0.988692 | 0.313141 | 42 | EmberJS-TodoMVC |117.796667 |118.086667 |1.002462 | 0.558244 | 43 | Flight-TodoMVC |64.273333 |62.260000 |0.968675 | 0.000000 (significant) | 44 | BackboneJS-TodoMVC |42.856667 |42.863333 |1.000156 | 0.975025 | 45 | Preact-TodoMVC |16.326667 |16.673333 |1.021233 | 0.298674 | 46 | AngularJS-TodoMVC |123.146667 |122.413333 |0.994045 | 0.160282 | 47 | Inferno-TodoMVC |57.510000 |57.533333 |1.000406 | 0.947767 | 48 | Vanilla-ES2015-TodoMVC |61.133333 |59.200000 |0.968375 | 0.000000 (significant) | 49 | Angular2-TypeScript-TodoMVC |38.863333 |38.963333 |1.002573 | 0.860359 | 50 | VanillaJS-TodoMVC |51.296667 |49.423333 |0.963480 | 0.000000 (significant) | 51 | jQuery-TodoMVC |210.933333 |210.596667 |0.998404 | 0.590132 | 52 | EmberJS-Debug-TodoMVC |326.093333 |324.890000 |0.996310 | 0.156955 | 53 | React-TodoMVC |81.113333 |81.360000 |1.003041 | 0.335615 | 54 | React-Redux-TodoMVC |132.560000 |132.256667 |0.997712 | 0.306072 | 55 | Vanilla-ES2015-Babel-Webpack-TodoMVC |60.073333 |59.026667 |0.982577 | 0.000883 (significant) | 56 ---------------------------------------------------------------------------------------------------------------------------------- 57 a mean = 280.29390 58 b mean = 282.51413 59 pValue = 0.0000083325 60 (Bigger means are better.) 61 1.008 times better 62 Results ARE significant 63 64 * CMakeLists.txt: 65 * JavaScriptCore.xcodeproj/project.pbxproj: 66 * heap/Heap.cpp: 67 (JSC::Heap::finalize): 68 * runtime/JSONAtomStringCache.h: Added. 69 (JSC::JSONAtomStringCache::makeIdentifier): 70 (JSC::JSONAtomStringCache::clear): 71 (JSC::JSONAtomStringCache::cacheSlot): 72 (JSC::JSONAtomStringCache::cache): 73 * runtime/JSONAtomStringCacheInlines.h: Added. 74 (JSC::JSONAtomStringCache::make): 75 (JSC::JSONAtomStringCache::vm const): 76 * runtime/LiteralParser.cpp: 77 (JSC::LiteralParser<CharType>::makeIdentifier): 78 (JSC::LiteralParser<CharType>::makeJSString): 79 (JSC::LiteralParser<CharType>::parsePrimitiveValue): 80 (JSC::LiteralParser<CharType>::parse): 81 * runtime/LiteralParser.h: 82 * runtime/VM.h: 83 1 84 2021-11-15 Yusuke Suzuki <ysuzuki@apple.com> 2 85 -
trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
r285795 r285955 1876 1876 E32D4DE926DAFD4300D4533A /* TemporalCalendar.h in Headers */ = {isa = PBXBuildFile; fileRef = E32D4DE326DAFD4300D4533A /* TemporalCalendar.h */; }; 1877 1877 E32D4DEA26DAFD4300D4533A /* TemporalCalendarConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = E32D4DE426DAFD4300D4533A /* TemporalCalendarConstructor.h */; }; 1878 E32FEA2C27448F3700FF41C1 /* JSONAtomStringCacheInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E32FEA2A27448F3600FF41C1 /* JSONAtomStringCacheInlines.h */; }; 1879 E32FEA2D27448F3700FF41C1 /* JSONAtomStringCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E32FEA2B27448F3600FF41C1 /* JSONAtomStringCache.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1878 1880 E33095DD23210A1B00EB7856 /* JSInternalFieldObjectImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E33095DC23210A1400EB7856 /* JSInternalFieldObjectImpl.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1879 1881 E334CBB521FD96A9000EB178 /* RegExpGlobalData.h in Headers */ = {isa = PBXBuildFile; fileRef = E334CBB321FD96A9000EB178 /* RegExpGlobalData.h */; settings = {ATTRIBUTES = (Private, ); }; }; … … 5177 5179 E32D4DE426DAFD4300D4533A /* TemporalCalendarConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemporalCalendarConstructor.h; sourceTree = "<group>"; }; 5178 5180 E32D4DE526DAFD4300D4533A /* TemporalCalendarConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TemporalCalendarConstructor.cpp; sourceTree = "<group>"; }; 5181 E32FEA2A27448F3600FF41C1 /* JSONAtomStringCacheInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAtomStringCacheInlines.h; sourceTree = "<group>"; }; 5182 E32FEA2B27448F3600FF41C1 /* JSONAtomStringCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONAtomStringCache.h; sourceTree = "<group>"; }; 5179 5183 E3305FB020B0F78700CEB82B /* InByVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InByVariant.cpp; sourceTree = "<group>"; }; 5180 5184 E3305FB120B0F78800CEB82B /* InByVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InByVariant.h; sourceTree = "<group>"; }; … … 7810 7814 BC22A3990E16E14800AF21C8 /* JSObject.h */, 7811 7815 0F93275E1C21EF7F00CF6564 /* JSObjectInlines.h */, 7816 E32FEA2B27448F3600FF41C1 /* JSONAtomStringCache.h */, 7817 E32FEA2A27448F3600FF41C1 /* JSONAtomStringCacheInlines.h */, 7812 7818 A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */, 7813 7819 A7F9935D0FD7325100A0B2D0 /* JSONObject.h */, … … 10431 10437 BC18C4250E16F5CD00B34460 /* JSObjectRef.h in Headers */, 10432 10438 A7280A2811557E3000D56957 /* JSObjectRefPrivate.h in Headers */, 10439 E32FEA2D27448F3700FF41C1 /* JSONAtomStringCache.h in Headers */, 10440 E32FEA2C27448F3700FF41C1 /* JSONAtomStringCacheInlines.h in Headers */, 10433 10441 A7F9935F0FD7325100A0B2D0 /* JSONObject.h in Headers */, 10434 10442 BC87CDB910712AD4000614CF /* JSONObject.lut.h in Headers */, -
trunk/Source/JavaScriptCore/heap/Heap.cpp
r285653 r285955 2049 2049 cache->clear(); 2050 2050 2051 if (m_lastCollectionScope && m_lastCollectionScope.value() == CollectionScope::Full) 2052 vm().jsonAtomStringCache.clear(); 2053 2051 2054 immutableButterflyToStringCache.clear(); 2052 2055 -
trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp
r282468 r285955 31 31 #include "JSArray.h" 32 32 #include "JSCInlines.h" 33 #include "JSONAtomStringCacheInlines.h" 33 34 #include "Lexer.h" 34 35 #include "ObjectConstructor.h" … … 144 145 145 146 template <typename CharType> 146 ALWAYS_INLINE Identifier LiteralParser<CharType>::makeIdentifier( typename Lexer::LiteralParserTokenPtr token)147 ALWAYS_INLINE Identifier LiteralParser<CharType>::makeIdentifier(VM& vm, typename Lexer::LiteralParserTokenPtr token) 147 148 { 148 149 if (token->stringIs8Bit) 149 return makeIdentifier(token->stringToken8, token->stringLength); 150 return makeIdentifier(token->stringToken16, token->stringLength); 151 } 152 153 150 return Identifier::fromString(vm, vm.jsonAtomStringCache.makeIdentifier(token->stringToken8, token->stringLength)); 151 return Identifier::fromString(vm, vm.jsonAtomStringCache.makeIdentifier(token->stringToken16, token->stringLength)); 152 } 153 154 154 template <typename CharType> 155 template <typename LiteralCharType> 156 ALWAYS_INLINE Identifier LiteralParser<CharType>::makeIdentifier(const LiteralCharType* characters, size_t length) 157 { 158 VM& vm = m_globalObject->vm(); 159 if (!length) 160 return vm.propertyNames->emptyIdentifier; 161 162 auto firstCharacter = characters[0]; 163 if (length == 1) { 164 if constexpr (sizeof(LiteralCharType) == 1) 165 return Identifier::fromString(vm, vm.smallStrings.singleCharacterStringRep(firstCharacter)); 166 if (firstCharacter <= maxSingleCharacterString) 167 return Identifier::fromString(vm, vm.smallStrings.singleCharacterStringRep(firstCharacter)); 168 return Identifier::fromString(vm, characters, length); 169 } 170 171 if (firstCharacter >= maximumCachableCharacter) 172 return Identifier::fromString(vm, characters, length); 173 174 // 0 means no entry since m_recentIdentifiersIndex is zero-filled initially. 175 uint8_t indexPlusOne = m_recentIdentifiersIndex[firstCharacter]; 176 if (indexPlusOne) { 177 uint8_t index = indexPlusOne - 1; 178 auto& ident = m_recentIdentifiers[index]; 179 if (Identifier::equal(ident.impl(), characters, length)) 180 return ident; 181 auto result = Identifier::fromString(vm, characters, length); 182 m_recentIdentifiers[index] = result; 183 return result; 184 } 185 186 auto result = Identifier::fromString(vm, characters, length); 187 m_recentIdentifiers.uncheckedAppend(result); 188 indexPlusOne = m_recentIdentifiers.size(); 189 m_recentIdentifiersIndex[firstCharacter] = indexPlusOne; 190 return result; 155 ALWAYS_INLINE JSString* LiteralParser<CharType>::makeJSString(VM& vm, typename Lexer::LiteralParserTokenPtr token) 156 { 157 constexpr unsigned maxAtomizeStringLength = 10; 158 if (token->stringIs8Bit) { 159 if (token->stringLength > maxAtomizeStringLength) 160 return jsString(vm, String(token->stringToken8, token->stringLength)); 161 return jsString(vm, Identifier::fromString(vm, token->stringToken8, token->stringLength).string()); 162 } 163 if (token->stringLength > maxAtomizeStringLength) 164 return jsString(vm, String(token->stringToken16, token->stringLength)); 165 return jsString(vm, Identifier::fromString(vm, token->stringToken16, token->stringLength).string()); 191 166 } 192 167 … … 1145 1120 switch (m_lexer.currentToken()->type) { 1146 1121 case TokString: { 1147 JS Value result = jsString(vm, makeIdentifier(m_lexer.currentToken()).string());1122 JSString* result = makeJSString(vm, m_lexer.currentToken()); 1148 1123 m_lexer.next(); 1149 1124 return result; … … 1285 1260 if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { 1286 1261 while (true) { 1287 Identifier ident = makeIdentifier( m_lexer.currentToken());1262 Identifier ident = makeIdentifier(vm, m_lexer.currentToken()); 1288 1263 1289 1264 if (UNLIKELY(m_lexer.next() != TokColon)) { … … 1359 1334 return { }; 1360 1335 } 1361 identifierStack.append(makeIdentifier( m_lexer.currentToken()));1336 identifierStack.append(makeIdentifier(vm, m_lexer.currentToken())); 1362 1337 1363 1338 // Check for colon -
trunk/Source/JavaScriptCore/runtime/LiteralParser.h
r282468 r285955 198 198 JSValue parsePrimitiveValue(VM&); 199 199 200 ALWAYS_INLINE Identifier makeIdentifier(typename Lexer::LiteralParserTokenPtr); 201 template<typename LiteralCharType> 202 ALWAYS_INLINE Identifier makeIdentifier(const LiteralCharType* characters, size_t length); 200 ALWAYS_INLINE Identifier makeIdentifier(VM&, typename Lexer::LiteralParserTokenPtr); 201 ALWAYS_INLINE JSString* makeJSString(VM&, typename Lexer::LiteralParserTokenPtr); 203 202 204 203 void setErrorMessageForToken(TokenType); … … 209 208 ParserMode m_mode; 210 209 String m_parseErrorMessage; 211 static constexpr unsigned maximumCachableCharacter = 128;212 std::array<uint8_t, maximumCachableCharacter> m_recentIdentifiersIndex { };213 Vector<Identifier, maximumCachableCharacter> m_recentIdentifiers;214 210 }; 215 211 -
trunk/Source/JavaScriptCore/runtime/VM.h
r285795 r285955 48 48 #include "JSDateMath.h" 49 49 #include "JSLock.h" 50 #include "JSONAtomStringCache.h" 50 51 #include "MacroAssemblerCodeRef.h" 51 52 #include "Microtask.h" … … 806 807 WeakGCMap<StringImpl*, JSString, PtrHash<StringImpl*>> stringCache; 807 808 Strong<JSString> lastCachedString; 809 JSONAtomStringCache jsonAtomStringCache; 808 810 809 811 AtomStringTable* atomStringTable() const { return m_atomStringTable; }
Note: See TracChangeset
for help on using the changeset viewer.