- 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;