Title: [289020] trunk
Revision
289020
Author
[email protected]
Date
2022-02-02 17:03:52 -0800 (Wed, 02 Feb 2022)

Log Message

Speed-up JSON.stringify() by avoiding "toJSON" property lookups
https://bugs.webkit.org/show_bug.cgi?id=235996

Patch by Alexey Shvayka <[email protected]> on 2022-02-02
Reviewed by Saam Barati.

JSTests:

* microbenchmarks/json-stringify-many-objects-to-json.js:
Make this test a little more realistic by moving "toJSON" onto prototype.

* microbenchmarks/vanilla-todomvc-json-stringify.js: Added.

Source/_javascript_Core:

Speedometer2/Vanilla* subtests are highly reliant on JSON.stringify() for "storage":
it accounts for 10-15% of running time. EmberJS* subtests rely on JSON.stringify() as
well, although they are significantly slower overall, and also encounter only a few
different structures.

This patch caches "toJSON" properties on Structure's rare data; it's the same technique
we are using in toPrimitive() to avoid "toString" / "valueOf" lookups. The microbenchmark,
which was carefully extracted from Speedometer2/Vanilla* subtests, progressed by 3.7%.

While we could come up with a solution that doesn't involve creating StructureRareData
for all structures we stringify, like keeping a list of StructureIDs w/o "toJSON" method,
which will be correct as long as `m_hasFastObjectProperties || m_isJSArray` is true for all
seen objects, that would probably miss some edge case, won't persist between JSON.stringify()
calls, and won't speed-up structures with "toJSON" methods like Dates.

* runtime/CachedSpecialPropertyAdaptiveStructureWatchpoint.cpp:
(JSC::CachedSpecialPropertyAdaptiveStructureWatchpoint::fireInternal):
* runtime/JSONObject.cpp:
(JSC::Stringifier::toJSON):
* runtime/StructureRareData.cpp:
(JSC::StructureRareData::cacheSpecialPropertySlow):
(JSC::CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::handleFire):
* runtime/StructureRareData.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (289019 => 289020)


--- trunk/JSTests/ChangeLog	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/JSTests/ChangeLog	2022-02-03 01:03:52 UTC (rev 289020)
@@ -1,3 +1,15 @@
+2022-02-02  Alexey Shvayka  <[email protected]>
+
+        Speed-up JSON.stringify() by avoiding "toJSON" property lookups
+        https://bugs.webkit.org/show_bug.cgi?id=235996
+
+        Reviewed by Saam Barati.
+
+        * microbenchmarks/json-stringify-many-objects-to-json.js:
+        Make this test a little more realistic by moving "toJSON" onto prototype.
+
+        * microbenchmarks/vanilla-todomvc-json-stringify.js: Added.
+
 2022-02-02  Yusuke Suzuki  <[email protected]>
 
         [JSC] wasm atomic opcodes should be rejected if alignment is not equal to natural width

Modified: trunk/JSTests/microbenchmarks/json-stringify-many-objects-to-json.js (289019 => 289020)


--- trunk/JSTests/microbenchmarks/json-stringify-many-objects-to-json.js	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/JSTests/microbenchmarks/json-stringify-many-objects-to-json.js	2022-02-03 01:03:52 UTC (rev 289020)
@@ -1,6 +1,7 @@
-const toJSON = () => '';
+function C() {}
+C.prototype.toJSON = () => '';
 const value = {};
 for (let i = 0; i < 100; ++i)
-    value[i] = {toJSON};
+    value["k" + i] = new C;
 for (let i = 0; i < 1e5 / 4; ++i)
     JSON.stringify(value);

Added: trunk/JSTests/microbenchmarks/vanilla-todomvc-json-stringify.js (0 => 289020)


--- trunk/JSTests/microbenchmarks/vanilla-todomvc-json-stringify.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/vanilla-todomvc-json-stringify.js	2022-02-03 01:03:52 UTC (rev 289020)
@@ -0,0 +1,13 @@
+(function() {
+    var todos = new Array(67);
+    for (var i = 0; i < 67; i++)
+        todos[i] = { title: "Something to do", completed: true, id: i };
+    var state = { todos };
+
+    var json;
+    for (var j = 0; j < 2e4; j++)
+        json = JSON.stringify(state);
+
+    if (json.length !== 3552)
+        throw new Error("Bad assertion!");
+})();

Modified: trunk/Source/_javascript_Core/ChangeLog (289019 => 289020)


--- trunk/Source/_javascript_Core/ChangeLog	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/Source/_javascript_Core/ChangeLog	2022-02-03 01:03:52 UTC (rev 289020)
@@ -1,3 +1,34 @@
+2022-02-02  Alexey Shvayka  <[email protected]>
+
+        Speed-up JSON.stringify() by avoiding "toJSON" property lookups
+        https://bugs.webkit.org/show_bug.cgi?id=235996
+
+        Reviewed by Saam Barati.
+
+        Speedometer2/Vanilla* subtests are highly reliant on JSON.stringify() for "storage":
+        it accounts for 10-15% of running time. EmberJS* subtests rely on JSON.stringify() as
+        well, although they are significantly slower overall, and also encounter only a few
+        different structures.
+
+        This patch caches "toJSON" properties on Structure's rare data; it's the same technique
+        we are using in toPrimitive() to avoid "toString" / "valueOf" lookups. The microbenchmark,
+        which was carefully extracted from Speedometer2/Vanilla* subtests, progressed by 3.7%.
+
+        While we could come up with a solution that doesn't involve creating StructureRareData
+        for all structures we stringify, like keeping a list of StructureIDs w/o "toJSON" method,
+        which will be correct as long as `m_hasFastObjectProperties || m_isJSArray` is true for all
+        seen objects, that would probably miss some edge case, won't persist between JSON.stringify()
+        calls, and won't speed-up structures with "toJSON" methods like Dates.
+
+        * runtime/CachedSpecialPropertyAdaptiveStructureWatchpoint.cpp:
+        (JSC::CachedSpecialPropertyAdaptiveStructureWatchpoint::fireInternal):
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::toJSON):
+        * runtime/StructureRareData.cpp:
+        (JSC::StructureRareData::cacheSpecialPropertySlow):
+        (JSC::CachedSpecialPropertyAdaptiveInferredPropertyValueWatchpoint::handleFire):
+        * runtime/StructureRareData.h:
+
 2022-02-02  Elliott Williams  <[email protected]>
 
         [Xcode] Fix redundant execution of "Check VTables..." script

Modified: trunk/Source/_javascript_Core/runtime/CachedSpecialPropertyAdaptiveStructureWatchpoint.cpp (289019 => 289020)


--- trunk/Source/_javascript_Core/runtime/CachedSpecialPropertyAdaptiveStructureWatchpoint.cpp	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/Source/_javascript_Core/runtime/CachedSpecialPropertyAdaptiveStructureWatchpoint.cpp	2022-02-03 01:03:52 UTC (rev 289020)
@@ -64,6 +64,8 @@
         key = CachedSpecialPropertyKey::ToString;
     else if (m_key.uid() == vm.propertyNames->valueOf.impl())
         key = CachedSpecialPropertyKey::ValueOf;
+    else if (m_key.uid() == vm.propertyNames->toJSON.impl())
+        key = CachedSpecialPropertyKey::ToJSON;
     else {
         ASSERT(m_key.uid() == vm.propertyNames->toPrimitiveSymbol.impl());
         key = CachedSpecialPropertyKey::ToPrimitive;

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (289019 => 289020)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2022-02-03 01:03:52 UTC (rev 289020)
@@ -293,9 +293,21 @@
     auto scope = DECLARE_THROW_SCOPE(vm);
     scope.assertNoException();
 
-    JSValue toJSONFunction = baseValue.get(m_globalObject, vm.propertyNames->toJSON);
-    RETURN_IF_EXCEPTION(scope, { });
+    JSValue toJSONFunction;
+    if (baseValue.isObject())
+        toJSONFunction = asObject(baseValue)->structure(vm)->cachedSpecialProperty(CachedSpecialPropertyKey::ToJSON);
 
+    if (!toJSONFunction) {
+        PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
+        bool hasProperty = baseValue.getPropertySlot(m_globalObject, vm.propertyNames->toJSON, slot);
+        RETURN_IF_EXCEPTION(scope, { });
+        toJSONFunction = hasProperty ? slot.getValue(m_globalObject, vm.propertyNames->toJSON) : jsUndefined();
+        RETURN_IF_EXCEPTION(scope, { });
+
+        if (baseValue.isObject())
+            asObject(baseValue)->structure(vm)->cacheSpecialProperty(m_globalObject, vm, toJSONFunction, CachedSpecialPropertyKey::ToJSON, slot);
+    }
+
     auto callData = getCallData(vm, toJSONFunction);
     if (callData.type == CallData::Type::None)
         return baseValue;

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.cpp (289019 => 289020)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2022-02-03 01:03:52 UTC (rev 289020)
@@ -135,6 +135,9 @@
     case CachedSpecialPropertyKey::ToPrimitive:
         uid = vm.propertyNames->toPrimitiveSymbol.impl();
         break;
+    case CachedSpecialPropertyKey::ToJSON:
+        uid = vm.propertyNames->toJSON.impl();
+        break;
     }
 
     if (!ownStructure->propertyAccessesAreCacheable() || ownStructure->isProxy()) {
@@ -267,6 +270,8 @@
         key = CachedSpecialPropertyKey::ToString;
     else if (this->key().uid() == vm.propertyNames->valueOf.impl())
         key = CachedSpecialPropertyKey::ValueOf;
+    else if (this->key().uid() == vm.propertyNames->toJSON.impl())
+        key = CachedSpecialPropertyKey::ToJSON;
     else {
         ASSERT(this->key().uid() == vm.propertyNames->toPrimitiveSymbol.impl());
         key = CachedSpecialPropertyKey::ToPrimitive;

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.h (289019 => 289020)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.h	2022-02-03 00:59:08 UTC (rev 289019)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.h	2022-02-03 01:03:52 UTC (rev 289020)
@@ -52,8 +52,9 @@
     ToString,
     ValueOf,
     ToPrimitive,
+    ToJSON,
 };
-static constexpr unsigned numberOfCachedSpecialPropertyKeys = 4;
+static constexpr unsigned numberOfCachedSpecialPropertyKeys = 5;
 
 class StructureRareData;
 class StructureChainInvalidationWatchpoint;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to