⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 201436 in webkit


Ignore:
Timestamp:
May 26, 2016, 3:30:05 PM (10 years ago)
Author:
ggaren@apple.com
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.

Location:
trunk/Source/JavaScriptCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r201428 r201436  
     12016-05-26  Geoffrey Garen  <ggaren@apple.com>
     2
     3        REGRESSION: JSBench spends a lot of time transitioning to/from dictionary
     4        https://bugs.webkit.org/show_bug.cgi?id=158045
     5
     6        Reviewed by Saam Barati.
     7
     8        15% speedup on jsbench-amazon-firefox, possibly 5% speedup overall on jsbench.
     9
     10        This regression seems to have two parts:
     11
     12        (1) Transitioning the window object to/from dictionary is more expensive
     13        than it used to be to because the window object has lots more properties.
     14        The window object has more properties because, for WebIDL compatibility,
     15        we reify DOM APIs as properties when you delete.
     16
     17        (2) DOM prototypes transition to/from dictionary upon creation
     18        because, once again for WebIDL compatibility, we reify their static
     19        APIs eagerly.
     20
     21        The solution is to chill out a bit on dictionary transitions.
     22
     23        * bytecode/ObjectPropertyConditionSet.cpp: Don't flatten a dictionary
     24        if we've already done so before. This avoids pathological churn, and it
     25        is our idiom in other places.
     26
     27        * interpreter/Interpreter.cpp:
     28        (JSC::Interpreter::execute): Do flatten the global object unconditionally
     29        if it is an uncacheable dictionary because the global object is super
     30        important.
     31
     32        * runtime/BatchedTransitionOptimizer.h:
     33        (JSC::BatchedTransitionOptimizer::BatchedTransitionOptimizer):
     34        (JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer): Deleted.
     35        Don't transition away from dictionary after a batched set of property
     36        puts because normal dictionaries are cacheable and that's a perfectly
     37        fine state to be in -- and the transition is expensive.
     38
     39        * runtime/JSGlobalObject.cpp:
     40        (JSC::JSGlobalObject::init): Do start the global object out as a cacheable
     41        dictionary because it will inevitably have enough properties to become
     42        a dictionary.
     43
     44        * runtime/Operations.h:
     45        (JSC::normalizePrototypeChain): Same as ObjectPropertyConditionSet.cpp.
     46
    1472016-05-25  Geoffrey Garen  <ggaren@apple.com>
    248
  • trunk/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp

    r201363 r201436  
    264264        structure = object->structure(vm);
    265265       
    266         // Since we're accessing a prototype repeatedly, it's a good bet that it should not be
    267         // treated as a dictionary.
    268266        if (structure->isDictionary()) {
    269267            if (concurrency == MainThread) {
     268                if (structure->hasBeenFlattenedBefore()) {
     269                    if (verbose)
     270                        dataLog("Dictionary has been flattened before, so invalid.\n");
     271                    return ObjectPropertyConditionSet::invalid();
     272                }
    270273                if (verbose)
    271274                    dataLog("Flattening ", pointerDump(structure));
  • trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r201328 r201436  
    939939        return throwTerminatedExecutionException(callFrame);
    940940
     941    if (scope->structure()->isUncacheableDictionary())
     942        scope->flattenDictionaryObject(vm);
     943
    941944    ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
    942945
     
    11871190    }
    11881191
     1192    if (variableObject->structure()->isUncacheableDictionary())
     1193        variableObject->flattenDictionaryObject(vm);
     1194
    11891195    if (numVariables || numFunctions) {
    11901196        BatchedTransitionOptimizer optimizer(vm, variableObject);
     
    12431249    if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
    12441250        return throwTerminatedExecutionException(callFrame);
     1251
     1252    if (scope->structure()->isUncacheableDictionary())
     1253        scope->flattenDictionaryObject(vm);
    12451254
    12461255    ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
  • trunk/Source/JavaScriptCore/runtime/BatchedTransitionOptimizer.h

    r169703 r201436  
    3636public:
    3737    BatchedTransitionOptimizer(VM& vm, JSObject* object)
    38         : m_vm(&vm)
    39         , m_object(object)
    4038    {
    41         if (!m_object->structure(vm)->isDictionary())
    42             m_object->convertToDictionary(vm);
     39        if (!object->structure(vm)->isDictionary())
     40            object->convertToDictionary(vm);
    4341    }
    44 
    45     ~BatchedTransitionOptimizer()
    46     {
    47         if (m_object->structure()->isDictionary())
    48             m_object->flattenDictionaryObject(*m_vm);
    49     }
    50 
    51 private:
    52     VM* m_vm;
    53     JSObject* m_object;
    5442};
    5543
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp

    r201340 r201436  
    320320    ASSERT(vm.currentThreadIsHoldingAPILock());
    321321
     322    Base::setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure()));
     323
    322324    JSGlobalObject::globalExec()->init(0, 0, CallFrame::noCaller(), 0, 0);
    323325
  • trunk/Source/JavaScriptCore/runtime/Operations.cpp

    r197614 r201436  
    121121}
    122122
     123size_t normalizePrototypeChain(CallFrame* callFrame, Structure* structure)
     124{
     125    VM& vm = callFrame->vm();
     126    size_t count = 0;
     127    while (1) {
     128        if (structure->isProxy())
     129            return InvalidPrototypeChain;
     130        JSValue v = structure->prototypeForLookup(callFrame);
     131        if (v.isNull())
     132            return count;
     133
     134        JSCell* base = v.asCell();
     135        structure = base->structure(vm);
     136        if (structure->isDictionary()) {
     137            if (structure->hasBeenFlattenedBefore())
     138                return InvalidPrototypeChain;
     139            structure->flattenDictionaryStructure(vm, asObject(base));
     140        }
     141
     142        ++count;
     143    }
     144}
     145
    123146} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/Operations.h

    r187780 r201436  
    2828
    2929namespace JSC {
     30
     31#define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
    3032
    3133NEVER_INLINE JSValue jsAddSlowCase(CallFrame*, JSValue, JSValue);
     
    3436bool jsIsObjectTypeOrNull(CallFrame*, JSValue);
    3537bool jsIsFunctionType(JSValue);
     38size_t normalizePrototypeChain(CallFrame*, Structure*);
    3639
    3740ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2)
     
    193196}
    194197
    195 #define InvalidPrototypeChain (std::numeric_limits<size_t>::max())
    196 
    197 inline size_t normalizePrototypeChain(CallFrame* callFrame, Structure* structure)
    198 {
    199     VM& vm = callFrame->vm();
    200     size_t count = 0;
    201     while (1) {
    202         if (structure->isProxy())
    203             return InvalidPrototypeChain;
    204         JSValue v = structure->prototypeForLookup(callFrame);
    205         if (v.isNull())
    206             return count;
    207 
    208         JSCell* base = v.asCell();
    209         structure = base->structure(vm);
    210         // Since we're accessing a prototype in a loop, it's a good bet that it
    211         // should not be treated as a dictionary.
    212         if (structure->isDictionary())
    213             structure->flattenDictionaryStructure(vm, asObject(base));
    214 
    215         ++count;
    216     }
    217 }
    218 
    219198} // namespace JSC
    220199
Note: See TracChangeset for help on using the changeset viewer.