Title: [201436] trunk/Source/_javascript_Core
Revision
201436
Author
[email protected]
Date
2016-05-26 15:30:05 -0700 (Thu, 26 May 2016)

Log Message

REGRESSION: JSBench spends a lot of time transitioning to/from dictionary
https://bugs.webkit.org/show_bug.cgi?id=158045

Reviewed by Saam Barati.

15% speedup on jsbench-amazon-firefox, possibly 5% speedup overall on jsbench.

This regression seems to have two parts:

(1) Transitioning the window object to/from dictionary is more expensive
than it used to be to because the window object has lots more properties.
The window object has more properties because, for WebIDL compatibility,
we reify DOM APIs as properties when you delete.

(2) DOM prototypes transition to/from dictionary upon creation
because, once again for WebIDL compatibility, we reify their static
APIs eagerly.

The solution is to chill out a bit on dictionary transitions.

* bytecode/ObjectPropertyConditionSet.cpp: Don't flatten a dictionary
if we've already done so before. This avoids pathological churn, and it
is our idiom in other places.

* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute): Do flatten the global object unconditionally
if it is an uncacheable dictionary because the global object is super
important.

* runtime/BatchedTransitionOptimizer.h:
(JSC::BatchedTransitionOptimizer::BatchedTransitionOptimizer):
(JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer): Deleted.
Don't transition away from dictionary after a batched set of property
puts because normal dictionaries are cacheable and that's a perfectly
fine state to be in -- and the transition is expensive.

* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init): Do start the global object out as a cacheable
dictionary because it will inevitably have enough properties to become
a dictionary.

* runtime/Operations.h:
(JSC::normalizePrototypeChain): Same as ObjectPropertyConditionSet.cpp.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (201435 => 201436)


--- trunk/Source/_javascript_Core/ChangeLog	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-05-26 22:30:05 UTC (rev 201436)
@@ -1,3 +1,49 @@
+2016-05-26  Geoffrey Garen  <[email protected]>
+
+        REGRESSION: JSBench spends a lot of time transitioning to/from dictionary
+        https://bugs.webkit.org/show_bug.cgi?id=158045
+
+        Reviewed by Saam Barati.
+
+        15% speedup on jsbench-amazon-firefox, possibly 5% speedup overall on jsbench.
+
+        This regression seems to have two parts:
+
+        (1) Transitioning the window object to/from dictionary is more expensive
+        than it used to be to because the window object has lots more properties.
+        The window object has more properties because, for WebIDL compatibility,
+        we reify DOM APIs as properties when you delete.
+
+        (2) DOM prototypes transition to/from dictionary upon creation
+        because, once again for WebIDL compatibility, we reify their static
+        APIs eagerly.
+
+        The solution is to chill out a bit on dictionary transitions.
+
+        * bytecode/ObjectPropertyConditionSet.cpp: Don't flatten a dictionary
+        if we've already done so before. This avoids pathological churn, and it
+        is our idiom in other places.
+
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::execute): Do flatten the global object unconditionally
+        if it is an uncacheable dictionary because the global object is super
+        important.
+
+        * runtime/BatchedTransitionOptimizer.h:
+        (JSC::BatchedTransitionOptimizer::BatchedTransitionOptimizer):
+        (JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer): Deleted.
+        Don't transition away from dictionary after a batched set of property
+        puts because normal dictionaries are cacheable and that's a perfectly
+        fine state to be in -- and the transition is expensive.
+
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init): Do start the global object out as a cacheable
+        dictionary because it will inevitably have enough properties to become
+        a dictionary.
+
+        * runtime/Operations.h:
+        (JSC::normalizePrototypeChain): Same as ObjectPropertyConditionSet.cpp.
+
 2016-05-25  Geoffrey Garen  <[email protected]>
 
         replaceable own properties seem to ignore replacement after property caching

Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp (201435 => 201436)


--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp	2016-05-26 22:30:05 UTC (rev 201436)
@@ -263,10 +263,13 @@
         JSObject* object = jsCast<JSObject*>(value);
         structure = object->structure(vm);
         
-        // Since we're accessing a prototype repeatedly, it's a good bet that it should not be
-        // treated as a dictionary.
         if (structure->isDictionary()) {
             if (concurrency == MainThread) {
+                if (structure->hasBeenFlattenedBefore()) {
+                    if (verbose)
+                        dataLog("Dictionary has been flattened before, so invalid.\n");
+                    return ObjectPropertyConditionSet::invalid();
+                }
                 if (verbose)
                     dataLog("Flattening ", pointerDump(structure));
                 structure->flattenDictionaryStructure(vm, object);

Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (201435 => 201436)


--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2016-05-26 22:30:05 UTC (rev 201436)
@@ -938,6 +938,9 @@
     if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
         return throwTerminatedExecutionException(callFrame);
 
+    if (scope->structure()->isUncacheableDictionary())
+        scope->flattenDictionaryObject(vm);
+
     ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
 
     ProtoCallFrame protoCallFrame;
@@ -1186,6 +1189,9 @@
         }
     }
 
+    if (variableObject->structure()->isUncacheableDictionary())
+        variableObject->flattenDictionaryObject(vm);
+
     if (numVariables || numFunctions) {
         BatchedTransitionOptimizer optimizer(vm, variableObject);
         if (variableObject->next())
@@ -1243,6 +1249,9 @@
     if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
         return throwTerminatedExecutionException(callFrame);
 
+    if (scope->structure()->isUncacheableDictionary())
+        scope->flattenDictionaryObject(vm);
+
     ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
 
     // The |this| of the module is always `undefined`.

Modified: trunk/Source/_javascript_Core/runtime/BatchedTransitionOptimizer.h (201435 => 201436)


--- trunk/Source/_javascript_Core/runtime/BatchedTransitionOptimizer.h	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/runtime/BatchedTransitionOptimizer.h	2016-05-26 22:30:05 UTC (rev 201436)
@@ -35,22 +35,10 @@
     WTF_MAKE_NONCOPYABLE(BatchedTransitionOptimizer);
 public:
     BatchedTransitionOptimizer(VM& vm, JSObject* object)
-        : m_vm(&vm)
-        , m_object(object)
     {
-        if (!m_object->structure(vm)->isDictionary())
-            m_object->convertToDictionary(vm);
+        if (!object->structure(vm)->isDictionary())
+            object->convertToDictionary(vm);
     }
-
-    ~BatchedTransitionOptimizer()
-    {
-        if (m_object->structure()->isDictionary())
-            m_object->flattenDictionaryObject(*m_vm);
-    }
-
-private:
-    VM* m_vm;
-    JSObject* m_object;
 };
 
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (201435 => 201436)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-05-26 22:30:05 UTC (rev 201436)
@@ -319,6 +319,8 @@
 {
     ASSERT(vm.currentThreadIsHoldingAPILock());
 
+    Base::setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure()));
+
     JSGlobalObject::globalExec()->init(0, 0, CallFrame::noCaller(), 0, 0);
 
     m_debugger = 0;

Modified: trunk/Source/_javascript_Core/runtime/Operations.cpp (201435 => 201436)


--- trunk/Source/_javascript_Core/runtime/Operations.cpp	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/runtime/Operations.cpp	2016-05-26 22:30:05 UTC (rev 201436)
@@ -120,4 +120,27 @@
     return false;
 }
 
+size_t normalizePrototypeChain(CallFrame* callFrame, Structure* structure)
+{
+    VM& vm = callFrame->vm();
+    size_t count = 0;
+    while (1) {
+        if (structure->isProxy())
+            return InvalidPrototypeChain;
+        JSValue v = structure->prototypeForLookup(callFrame);
+        if (v.isNull())
+            return count;
+
+        JSCell* base = v.asCell();
+        structure = base->structure(vm);
+        if (structure->isDictionary()) {
+            if (structure->hasBeenFlattenedBefore())
+                return InvalidPrototypeChain;
+            structure->flattenDictionaryStructure(vm, asObject(base));
+        }
+
+        ++count;
+    }
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/Operations.h (201435 => 201436)


--- trunk/Source/_javascript_Core/runtime/Operations.h	2016-05-26 22:05:26 UTC (rev 201435)
+++ trunk/Source/_javascript_Core/runtime/Operations.h	2016-05-26 22:30:05 UTC (rev 201436)
@@ -28,11 +28,14 @@
 
 namespace JSC {
 
+#define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
+
 NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
 JSValue jsTypeStringForValue(CallFrame*, JSValue);
 JSValue jsTypeStringForValue(VM&, JSGlobalObject*, JSValue);
 bool jsIsObjectTypeOrNull(CallFrame*, JSValue);
 bool jsIsFunctionType(JSValue);
+size_t normalizePrototypeChain(CallFrame*, Structure*);
 
 ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
 {
@@ -192,30 +195,6 @@
     return jsAddSlowCase(callFrame, v1, v2);
 }
 
-#define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
-
-inline size_t normalizePrototypeChain(CallFrame* callFrame, Structure* structure)
-{
-    VM& vm = callFrame->vm();
-    size_t count = 0;
-    while (1) {
-        if (structure->isProxy())
-            return InvalidPrototypeChain;
-        JSValue v = structure->prototypeForLookup(callFrame);
-        if (v.isNull())
-            return count;
-
-        JSCell* base = v.asCell();
-        structure = base->structure(vm);
-        // Since we're accessing a prototype in a loop, it's a good bet that it
-        // should not be treated as a dictionary.
-        if (structure->isDictionary())
-            structure->flattenDictionaryStructure(vm, asObject(base));
-
-        ++count;
-    }
-}
-
 } // namespace JSC
 
 #endif // Operations_h
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to