Commit 9e127924 authored by darin@apple.com's avatar darin@apple.com

2008-06-28 Darin Adler <darin@apple.com>

        Reviewed by Cameron.

        - https://bugs.webkit.org/show_bug.cgi?id=19804
          optimize access to arrays without "holes"

        SunSpider says 1.8% faster.

        * kjs/JSArray.cpp:
        (KJS::JSArray::JSArray): Initialize m_fastAccessCutoff when creating
        arrays. Also updated for new location of m_vectorLength.
        (KJS::JSArray::getItem): Updated for new location of m_vectorLength.
        (KJS::JSArray::getSlowCase): Added. Broke out the non-hot parts of
        getOwnPropertySlot to make the hot part faster.
        (KJS::JSArray::getOwnPropertySlot): Added a new faster case for
        indices lower than m_fastAccessCutoff. We can do theese with no
        additional checks or branches.
        (KJS::JSArray::put): Added a new faster case for indices lower than
        m_fastAccessCutoff. We can do theese with no additional checks or
        branches. Moved the maxArrayIndex handling out of this function.
        Added code to set m_fastAccessCutoff when the very last hole in
        an array is filled; this is how the cutoff gets set for most arrays.
        (KJS::JSArray::putSlowCase): Moved the rest of the put function logic
        in here, to make the hot part of the put function faster.
        (KJS::JSArray::deleteProperty): Added code to lower m_fastAccessCutoff
        when a delete makes a new hole in the array.
        (KJS::JSArray::getPropertyNames): Updated for new location of
        m_vectorLength.
        (KJS::JSArray::increaseVectorLength): Ditto.
        (KJS::JSArray::setLength): Added code to lower m_fastAccessCutoff
        when setLength makes the array smaller.
        (KJS::JSArray::mark): Updated for new location of m_vectorLength.
        (KJS::JSArray::sort): Ditto. Set m_fastAccessCutoff after moving
        all the holes to the end of the array.
        (KJS::JSArray::compactForSorting): Ditto.
        (KJS::JSArray::checkConsistency): Added consistency checks fro
        m_fastAccessCutoff and updated for the new location of m_vectorLength.

        * kjs/JSArray.h: Added declarations for slow case functions.
        Replaced m_vectorLength with m_fastAccessCutoff.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@34867 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 64b5b6cf
2008-06-28 Darin Adler <darin@apple.com>
Reviewed by Cameron.
- https://bugs.webkit.org/show_bug.cgi?id=19804
optimize access to arrays without "holes"
SunSpider says 1.8% faster.
* kjs/JSArray.cpp:
(KJS::JSArray::JSArray): Initialize m_fastAccessCutoff when creating
arrays. Also updated for new location of m_vectorLength.
(KJS::JSArray::getItem): Updated for new location of m_vectorLength.
(KJS::JSArray::getSlowCase): Added. Broke out the non-hot parts of
getOwnPropertySlot to make the hot part faster.
(KJS::JSArray::getOwnPropertySlot): Added a new faster case for
indices lower than m_fastAccessCutoff. We can do theese with no
additional checks or branches.
(KJS::JSArray::put): Added a new faster case for indices lower than
m_fastAccessCutoff. We can do theese with no additional checks or
branches. Moved the maxArrayIndex handling out of this function.
Added code to set m_fastAccessCutoff when the very last hole in
an array is filled; this is how the cutoff gets set for most arrays.
(KJS::JSArray::putSlowCase): Moved the rest of the put function logic
in here, to make the hot part of the put function faster.
(KJS::JSArray::deleteProperty): Added code to lower m_fastAccessCutoff
when a delete makes a new hole in the array.
(KJS::JSArray::getPropertyNames): Updated for new location of
m_vectorLength.
(KJS::JSArray::increaseVectorLength): Ditto.
(KJS::JSArray::setLength): Added code to lower m_fastAccessCutoff
when setLength makes the array smaller.
(KJS::JSArray::mark): Updated for new location of m_vectorLength.
(KJS::JSArray::sort): Ditto. Set m_fastAccessCutoff after moving
all the holes to the end of the array.
(KJS::JSArray::compactForSorting): Ditto.
(KJS::JSArray::checkConsistency): Added consistency checks fro
m_fastAccessCutoff and updated for the new location of m_vectorLength.
* kjs/JSArray.h: Added declarations for slow case functions.
Replaced m_vectorLength with m_fastAccessCutoff.
2008-06-28 Cameron Zwarich <cwzwarich@uwaterloo.ca>
Reviewed by Sam.
......
......@@ -37,6 +37,7 @@ namespace KJS {
typedef HashMap<unsigned, JSValue*> SparseArrayValueMap;
struct ArrayStorage {
unsigned m_vectorLength;
unsigned m_numValuesInVector;
SparseArrayValueMap* m_sparseValueMap;
void* lazyCreationData; // An JSArray subclass can use this to fill the vector lazily.
......@@ -86,8 +87,9 @@ JSArray::JSArray(JSObject* prototype, unsigned initialLength)
unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
m_length = initialLength;
m_vectorLength = initialCapacity;
m_fastAccessCutoff = 0;
m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
m_storage->m_vectorLength = initialCapacity;
Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue*));
......@@ -100,10 +102,11 @@ JSArray::JSArray(JSObject* prototype, const ArgList& list)
unsigned length = list.size();
m_length = length;
m_vectorLength = length;
m_fastAccessCutoff = length;
ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length)));
storage->m_vectorLength = length;
storage->m_numValuesInVector = length;
storage->m_sparseValueMap = 0;
......@@ -134,7 +137,7 @@ JSValue* JSArray::getItem(unsigned i) const
ArrayStorage* storage = m_storage;
if (i < m_vectorLength) {
if (i < storage->m_vectorLength) {
JSValue* value = storage->m_vector[i];
return value ? value : jsUndefined();
}
......@@ -152,17 +155,17 @@ JSValue* JSArray::lengthGetter(ExecState* exec, const Identifier&, const Propert
return jsNumber(exec, static_cast<JSArray*>(slot.slotBase())->m_length);
}
ALWAYS_INLINE bool JSArray::inlineGetOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
NEVER_INLINE bool JSArray::getOwnPropertySlotSlowCase(ExecState* exec, unsigned i, PropertySlot& slot)
{
ArrayStorage* storage = m_storage;
if (UNLIKELY(i >= m_length)) {
if (i >= m_length) {
if (i > maxArrayIndex)
return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
return false;
}
if (i < m_vectorLength) {
if (i < storage->m_vectorLength) {
JSValue*& valueSlot = storage->m_vector[i];
if (valueSlot) {
slot.setValueSlot(&valueSlot);
......@@ -191,14 +194,19 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName
bool isArrayIndex;
unsigned i = propertyName.toArrayIndex(&isArrayIndex);
if (isArrayIndex)
return inlineGetOwnPropertySlot(exec, i, slot);
return JSArray::getOwnPropertySlot(exec, i, slot);
return JSObject::getOwnPropertySlot(exec, propertyName, slot);
}
bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
{
return inlineGetOwnPropertySlot(exec, i, slot);
if (i < m_fastAccessCutoff) {
slot.setValueSlot(&m_storage->m_vector[i]);
return true;
}
return getOwnPropertySlotSlowCase(exec, i, slot);
}
// ECMA 15.4.5.1
......@@ -228,29 +236,46 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
{
checkConsistency();
if (i < m_fastAccessCutoff) {
m_storage->m_vector[i] = value;
checkConsistency();
return;
}
unsigned length = m_length;
if (i >= length) {
if (i > maxArrayIndex) {
put(exec, Identifier::from(exec, i), value);
return;
}
if (i >= length && i <= maxArrayIndex) {
length = i + 1;
m_length = length;
}
ArrayStorage* storage = m_storage;
if (i < m_vectorLength) {
JSValue*& valueSlot = storage->m_vector[i];
storage->m_numValuesInVector += !valueSlot;
if (i < m_storage->m_vectorLength) {
JSValue*& valueSlot = m_storage->m_vector[i];
if (valueSlot) {
valueSlot = value;
checkConsistency();
return;
}
valueSlot = value;
if (++m_storage->m_numValuesInVector == m_length)
m_fastAccessCutoff = m_length;
checkConsistency();
return;
}
putSlowCase(exec, i, value);
}
NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue* value)
{
ArrayStorage* storage = m_storage;
SparseArrayValueMap* map = storage->m_sparseValueMap;
if (i >= sparseArrayCutoff) {
if (i > maxArrayIndex) {
put(exec, Identifier::from(exec, i), value);
return;
}
// We miss some cases where we could compact the storage, such as a large array that is being filled from the end
// (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
if (!isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
......@@ -277,7 +302,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
// Decide how many values it would be best to move from the map.
unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
unsigned newVectorLength = increasedVectorLength(i + 1);
for (unsigned j = max(m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
for (unsigned j = max(storage->m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
newNumValuesInVector += map->contains(j);
if (i >= sparseArrayCutoff)
newNumValuesInVector -= map->contains(i);
......@@ -296,7 +321,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
unsigned vectorLength = m_vectorLength;
unsigned vectorLength = storage->m_vectorLength;
if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
for (unsigned j = vectorLength; j < newVectorLength; ++j)
storage->m_vector[j] = 0;
......@@ -311,7 +336,7 @@ void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
storage->m_vector[i] = value;
m_vectorLength = newVectorLength;
storage->m_vectorLength = newVectorLength;
storage->m_numValuesInVector = newNumValuesInVector;
m_storage = storage;
......@@ -338,13 +363,18 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i)
ArrayStorage* storage = m_storage;
if (i < m_vectorLength) {
if (i < storage->m_vectorLength) {
JSValue*& valueSlot = storage->m_vector[i];
bool hadValue = valueSlot;
if (!valueSlot) {
checkConsistency();
return false;
}
valueSlot = 0;
storage->m_numValuesInVector -= hadValue;
--storage->m_numValuesInVector;
if (m_fastAccessCutoff > i)
m_fastAccessCutoff = i;
checkConsistency();
return hadValue;
return true;
}
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
......@@ -374,7 +404,7 @@ void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames
ArrayStorage* storage = m_storage;
unsigned usedVectorLength = min(m_length, m_vectorLength);
unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
for (unsigned i = 0; i < usedVectorLength; ++i) {
if (storage->m_vector[i])
propertyNames.add(Identifier::from(exec, i));
......@@ -396,7 +426,7 @@ bool JSArray::increaseVectorLength(unsigned newLength)
ArrayStorage* storage = m_storage;
unsigned vectorLength = m_vectorLength;
unsigned vectorLength = storage->m_vectorLength;
ASSERT(newLength > vectorLength);
unsigned newVectorLength = increasedVectorLength(newLength);
......@@ -404,7 +434,7 @@ bool JSArray::increaseVectorLength(unsigned newLength)
if (!storage)
return false;
m_vectorLength = newVectorLength;
storage->m_vectorLength = newVectorLength;
for (unsigned i = vectorLength; i < newVectorLength; ++i)
storage->m_vector[i] = 0;
......@@ -422,7 +452,10 @@ void JSArray::setLength(unsigned newLength)
unsigned length = m_length;
if (newLength < length) {
unsigned usedVectorLength = min(length, m_vectorLength);
if (m_fastAccessCutoff > newLength)
m_fastAccessCutoff = newLength;
unsigned usedVectorLength = min(length, storage->m_vectorLength);
for (unsigned i = newLength; i < usedVectorLength; ++i) {
JSValue*& valueSlot = storage->m_vector[i];
bool hadValue = valueSlot;
......@@ -455,7 +488,7 @@ void JSArray::mark()
ArrayStorage* storage = m_storage;
unsigned usedVectorLength = min(m_length, m_vectorLength);
unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
for (unsigned i = 0; i < usedVectorLength; ++i) {
JSValue* value = storage->m_vector[i];
if (value && !value->marked())
......@@ -625,7 +658,7 @@ void JSArray::sort(ExecState* exec, JSValue* compareFunction, CallType callType,
if (!m_length)
return;
unsigned usedVectorLength = min(m_length, m_vectorLength);
unsigned usedVectorLength = min(m_length, m_storage->m_vectorLength);
AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
tree.abstractor().m_exec = exec;
......@@ -670,7 +703,7 @@ void JSArray::sort(ExecState* exec, JSValue* compareFunction, CallType callType,
if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
newUsedVectorLength += map->size();
if (newUsedVectorLength > m_vectorLength) {
if (newUsedVectorLength > m_storage->m_vectorLength) {
if (!increaseVectorLength(newUsedVectorLength)) {
exec->setException(Error::create(exec, GeneralError, "Out of memory"));
return;
......@@ -709,6 +742,7 @@ void JSArray::sort(ExecState* exec, JSValue* compareFunction, CallType callType,
for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
m_storage->m_vector[i] = 0;
m_fastAccessCutoff = newUsedVectorLength;
m_storage->m_numValuesInVector = newUsedVectorLength;
checkConsistency(SortConsistencyCheck);
......@@ -720,7 +754,7 @@ unsigned JSArray::compactForSorting()
ArrayStorage* storage = m_storage;
unsigned usedVectorLength = min(m_length, m_vectorLength);
unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
unsigned numDefined = 0;
unsigned numUndefined = 0;
......@@ -743,7 +777,7 @@ unsigned JSArray::compactForSorting()
if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
newUsedVectorLength += map->size();
if (newUsedVectorLength > m_vectorLength) {
if (newUsedVectorLength > storage->m_vectorLength) {
if (!increaseVectorLength(newUsedVectorLength))
return 0;
storage = m_storage;
......@@ -762,6 +796,7 @@ unsigned JSArray::compactForSorting()
for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
storage->m_vector[i] = 0;
m_fastAccessCutoff = newUsedVectorLength;
storage->m_numValuesInVector = newUsedVectorLength;
checkConsistency(SortConsistencyCheck);
......@@ -787,14 +822,18 @@ void JSArray::checkConsistency(ConsistencyCheckType type)
if (type == SortConsistencyCheck)
ASSERT(!m_storage->m_sparseValueMap);
ASSERT(m_fastAccessCutoff <= m_length);
ASSERT(m_fastAccessCutoff <= m_storage->m_numValuesInVector);
unsigned numValuesInVector = 0;
for (unsigned i = 0; i < m_vectorLength; ++i) {
for (unsigned i = 0; i < m_storage->m_vectorLength; ++i) {
if (JSValue* value = m_storage->m_vector[i]) {
ASSERT(i < m_length);
if (type != DestructorConsistencyCheck)
value->type(); // Likely to crash if the object was deallocated.
++numValuesInVector;
} else {
ASSERT(i >= m_fastAccessCutoff);
if (type == SortConsistencyCheck)
ASSERT(i >= m_storage->m_numValuesInVector);
}
......@@ -806,7 +845,7 @@ void JSArray::checkConsistency(ConsistencyCheckType type)
for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
unsigned index = it->first;
ASSERT(index < m_length);
ASSERT(index >= m_vectorLength);
ASSERT(index >= m_storage->m_vectorLength);
ASSERT(index <= maxArrayIndex);
ASSERT(it->second);
if (type != DestructorConsistencyCheck)
......
......@@ -63,7 +63,9 @@ namespace KJS {
virtual const ClassInfo* classInfo() const { return &info; }
static JSValue* lengthGetter(ExecState*, const Identifier&, const PropertySlot&);
bool inlineGetOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
bool getOwnPropertySlotSlowCase(ExecState*, unsigned propertyName, PropertySlot&);
void putSlowCase(ExecState*, unsigned propertyName, JSValue*);
bool increaseVectorLength(unsigned newLength);
......@@ -73,7 +75,7 @@ namespace KJS {
void checkConsistency(ConsistencyCheckType = NormalConsistencyCheck);
unsigned m_length;
unsigned m_vectorLength;
unsigned m_fastAccessCutoff;
ArrayStorage* m_storage;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment