Ignore:
Timestamp:
Oct 16, 2009, 5:28:19 PM (16 years ago)
Author:
[email protected]
Message:

Fast for-in enumeration: Cache JSPropertyNameIterator; cache JSStrings
in JSPropertyNameIterator; inline more code.

Patch by Geoffrey Garen <[email protected]> on 2009-10-16
Reviewed by Oliver Hunt.

1.024x as fast on SunSpider (fasta: 1.43x as fast).

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dump):

  • bytecode/Opcode.h:
  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitGetPropertyNames):
(JSC::BytecodeGenerator::emitNextPropertyName):

  • bytecompiler/BytecodeGenerator.h: Added a few extra operands to

op_get_pnames and op_next_pname so that we can track iteration state
in the register file instead of in the JSPropertyNameIterator. (To be
cacheable, the JSPropertyNameIterator must be stateless.)

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::tryCachePutByID):
(JSC::Interpreter::tryCacheGetByID): Updated for rename to
"normalizePrototypeChain" and removal of "isCacheable".

(JSC::Interpreter::privateExecute): Updated for in-RegisterFile
iteration state tracking.

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_get_pnames): Updated for in-RegisterFile
iteration state tracking.

(JSC::JIT::emit_op_next_pname): Inlined code generation for op_next_pname.

  • jit/JITStubs.cpp:

(JSC::JITThunks::tryCachePutByID):
(JSC::JITThunks::tryCacheGetByID): Updated for rename to
"normalizePrototypeChain" and removal of "isCacheable".

(JSC::DEFINE_STUB_FUNCTION):

  • jit/JITStubs.h:

(JSC::): Added has_property and to_object stubs. Removed op_next_pname
stub, since has_property is all we need anymore.

  • parser/Nodes.cpp:

(JSC::ForInNode::emitBytecode): Updated for in-RegisterFile
iteration state tracking.

  • runtime/JSCell.h:
  • runtime/JSObject.cpp:

(JSC::JSObject::getPropertyNames): Don't do caching at this layer
anymore, since we don't create a JSPropertyNameIterator at this layer.

  • runtime/JSPropertyNameIterator.cpp:

(JSC::JSPropertyNameIterator::create): Do do caching at this layer.
(JSC::JSPropertyNameIterator::get): Updated for in-RegisterFile
iteration state tracking.
(JSC::JSPropertyNameIterator::markChildren): Mark our JSStrings.

  • runtime/JSPropertyNameIterator.h:

(JSC::JSPropertyNameIterator::size):
(JSC::JSPropertyNameIterator::setCachedStructure):
(JSC::JSPropertyNameIterator::cachedStructure):
(JSC::JSPropertyNameIterator::setCachedPrototypeChain):
(JSC::JSPropertyNameIterator::cachedPrototypeChain):
(JSC::JSPropertyNameIterator::JSPropertyNameIterator):
(JSC::Structure::setEnumerationCache): Don't store iteration state in
a JSPropertyNameIterator. Do cache a JSPropertyNameIterator in a
Structure.

  • runtime/JSValue.h:

(JSC::asCell):

  • runtime/MarkStack.h: Make those mischievous #include gods happy.
  • runtime/ObjectConstructor.cpp:
  • runtime/Operations.h:

(JSC::normalizePrototypeChain): Renamed countPrototypeChainEntriesAndCheckForProxies
to normalizePrototypeChain, since it changes dictionary prototypes to
non-dictionary objects.

  • runtime/PropertyNameArray.cpp:

(JSC::PropertyNameArray::add):

  • runtime/PropertyNameArray.h:

(JSC::PropertyNameArrayData::PropertyNameArrayData):
(JSC::PropertyNameArray::data):
(JSC::PropertyNameArray::size):
(JSC::PropertyNameArray::begin):
(JSC::PropertyNameArray::end): Simplified some code here to help with
current and future refactoring.

  • runtime/Protect.h:
  • runtime/Structure.cpp:

(JSC::Structure::~Structure):
(JSC::Structure::addPropertyWithoutTransition):
(JSC::Structure::removePropertyWithoutTransition): No need to clear
the enumeration cache with adding / removing properties without
transition. It is an error to add / remove properties without transition
once an object has been observed, and we can ASSERT to catch that.

  • runtime/Structure.h:

(JSC::Structure::enumerationCache): Changed the enumeration cache to
hold a JSPropertyNameIterator.

  • runtime/StructureChain.cpp:
  • runtime/StructureChain.h:

(JSC::StructureChain::head): Removed StructureChain::isCacheable because
it was wrong-headed in two ways: (1) It gave up when a prototype was a
dictionary, but instead we want un-dictionary heavily accessed
prototypes; (2) It folded a test for hasDefaultGetPropertyNames() into
a generic test for "cacheable-ness", but hasDefaultGetPropertyNames()
is only releavant to for-in caching.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/JSObject.cpp

    r49694 r49717  
    444444void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
    445445{
    446     bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_structure->isDictionary());
    447 
    448     if (shouldCache) {
    449         if (PropertyNameArrayData* data = m_structure->enumerationCache()) {
    450             if (data->cachedPrototypeChain() == m_structure->prototypeChain(exec)) {
    451                 propertyNames.setData(data);
    452                 return;
    453             }
    454 
    455             m_structure->clearEnumerationCache();
    456         }
    457     }
    458 
    459446    getOwnPropertyNames(exec, propertyNames);
    460447
    461     if (prototype().isObject()) {
    462         propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
    463         JSObject* prototype = asObject(this->prototype());
    464         while(1) {
    465             if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
    466                 prototype->getPropertyNames(exec, propertyNames);
    467                 break;
    468             }
    469             prototype->getOwnPropertyNames(exec, propertyNames);
    470             JSValue nextProto = prototype->prototype();
    471             if (!nextProto.isObject())
    472                 break;
    473             prototype = asObject(nextProto);
    474         }
    475     }
    476 
    477     if (shouldCache) {
    478         StructureChain* protoChain = m_structure->prototypeChain(exec);
    479         if (!protoChain->isCacheable())
    480             return;
    481         RefPtr<PropertyNameArrayData> data = propertyNames.data();
    482         data->setCachedPrototypeChain(protoChain);
    483         data->setCachedStructure(m_structure);
    484         m_structure->setEnumerationCache(data.release());
     448    if (prototype().isNull())
     449        return;
     450
     451    JSObject* prototype = asObject(this->prototype());
     452    while(1) {
     453        if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
     454            prototype->getPropertyNames(exec, propertyNames);
     455            break;
     456        }
     457        prototype->getOwnPropertyNames(exec, propertyNames);
     458        JSValue nextProto = prototype->prototype();
     459        if (nextProto.isNull())
     460            break;
     461        prototype = asObject(nextProto);
    485462    }
    486463}
Note: See TracChangeset for help on using the changeset viewer.