Commit 879ab75a authored by ggaren's avatar ggaren

JavaScriptCore:

        Reviewed by Darin Adler.
        
        http://bugs.webkit.org/show_bug.cgi?id=15835

        Switched List implementation from a custom heap allocator to an inline
        Vector, for a disappointing .5% SunSpider speedup.
        
        Also renamed List::slice to List::getSlice because "get" is the 
        conventional prefix for functions returning a value through an out 
        parameter.

        * kjs/array_object.cpp:
        (KJS::ArrayProtoFunc::callAsFunction): Removed some redundant function
        calls and memory accesses.

        * kjs/bool_object.cpp:
        (BooleanObjectImp::construct): Removed questionable use of iterator.

        * kjs/list.cpp:
        * kjs/list.h: New List class, implemented in terms of Vector. Two 
        interesting differences:
            1. The inline capacity is 8, not 5. Many of the Lists constructed 
            during a SunSpider run are larger than 5; almost none are larger
            than 8.

            2. The growth factor is 4, not 2. Since we can guarantee that Lists
            aren't long-lived, we can grow them more aggressively, to avoid
            excessive copying.

        * kjs/regexp_object.cpp:
        (RegExpObjectImp::construct): Removed redundant function calls.

        * kjs/string_object.cpp:
        (KJS::StringObjectImp::construct): Removed questionable use of iterator.

        * wtf/Vector.h:
        (WTF::::uncheckedAppend): Added a fast, unchecked version of append.

WebCore:

        Reviewed by Darin Adler.
        
        http://bugs.webkit.org/show_bug.cgi?id=15835

        Small adaptations to new KJS::List class.

        * bindings/js/kjs_window.cpp:
        (KJS::WindowFunc::callAsFunction):
        (KJS::ScheduledAction::ScheduledAction):

WebKit:

        Reviewed by Darin Adler.
        
        http://bugs.webkit.org/show_bug.cgi?id=15835

        Small adaptations to new KJS::List class.

        * ForwardingHeaders/kjs/value.h: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@27448 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent f13f4b57
2007-11-05 Geoffrey Garen <ggaren@apple.com>
Reviewed by Darin Adler.
http://bugs.webkit.org/show_bug.cgi?id=15835
Switched List implementation from a custom heap allocator to an inline
Vector, for a disappointing .5% SunSpider speedup.
Also renamed List::slice to List::getSlice because "get" is the
conventional prefix for functions returning a value through an out
parameter.
* kjs/array_object.cpp:
(KJS::ArrayProtoFunc::callAsFunction): Removed some redundant function
calls and memory accesses.
* kjs/bool_object.cpp:
(BooleanObjectImp::construct): Removed questionable use of iterator.
* kjs/list.cpp:
* kjs/list.h: New List class, implemented in terms of Vector. Two
interesting differences:
1. The inline capacity is 8, not 5. Many of the Lists constructed
during a SunSpider run are larger than 5; almost none are larger
than 8.
2. The growth factor is 4, not 2. Since we can guarantee that Lists
aren't long-lived, we can grow them more aggressively, to avoid
excessive copying.
* kjs/regexp_object.cpp:
(RegExpObjectImp::construct): Removed redundant function calls.
* kjs/string_object.cpp:
(KJS::StringObjectImp::construct): Removed questionable use of iterator.
* wtf/Vector.h:
(WTF::::uncheckedAppend): Added a fast, unchecked version of append.
2007-11-05 Mark Rowe <mrowe@apple.com>
Reviewed by Alp Toker.
......
......@@ -123,6 +123,7 @@ __ZN3KJS11Interpreter6s_hookE
__ZN3KJS11Interpreter8evaluateERKNS_7UStringEiPKNS_5UCharEiPNS_7JSValueE
__ZN3KJS11Interpreter8evaluateERKNS_7UStringEiS3_PNS_7JSValueE
__ZN3KJS11InterpreterC1EPNS_14JSGlobalObjectE
__ZN3KJS11InterpreterC1EPNS_14JSGlobalObjectE
__ZN3KJS11InterpreterC1Ev
__ZN3KJS11InterpreterC2EPNS_14JSGlobalObjectE
__ZN3KJS11InterpreterD1Ev
......@@ -157,9 +158,8 @@ __ZN3KJS16RuntimeObjectImpC1EPNS_8Bindings8InstanceE
__ZN3KJS17PropertyNameArray3addERKNS_10IdentifierE
__ZN3KJS19InternalFunctionImp4infoE
__ZN3KJS19InternalFunctionImpC2EPNS_17FunctionPrototypeERKNS_10IdentifierE
__ZN3KJS4List6appendEPNS_7JSValueE
__ZN3KJS4List7releaseEv
__ZN3KJS4ListC1Ev
__ZN3KJS4List15expandAndAppendEPNS_7JSValueE
__ZN3KJS4List7markSetEv
__ZN3KJS6JSCell9getObjectEv
__ZN3KJS6JSCellnwEm
__ZN3KJS6JSLock12DropAllLocksC1Ev
......@@ -241,10 +241,11 @@ __ZNK3KJS12DateInstance7getTimeERdRi
__ZNK3KJS13ArrayInstance7getItemEj
__ZNK3KJS19InternalFunctionImp14implementsCallEv
__ZNK3KJS19InternalFunctionImp21implementsHasInstanceEv
__ZNK3KJS4List2atEi
__ZNK3KJS4List5sliceEiRS0_
__ZNK3KJS4List8getSliceEiRS0_
__ZNK3KJS6JSCell17getTruncatedInt32ERi
__ZNK3KJS6JSCell17getTruncatedInt32ERi
__ZNK3KJS6JSCell18getTruncatedUInt32ERj
__ZNK3KJS6JSCell18getTruncatedUInt32ERj
__ZNK3KJS6JSCell9getNumberERd
__ZNK3KJS6JSCell9getNumberEv
__ZNK3KJS6JSCell9getStringERNS_7UStringE
......@@ -266,6 +267,7 @@ __ZNK3KJS8JSObject11hasPropertyEPNS_9ExecStateERKNS_10IdentifierE
__ZNK3KJS8JSObject12defaultValueEPNS_9ExecStateENS_6JSTypeE
__ZNK3KJS8JSObject14implementsCallEv
__ZNK3KJS8JSObject18getPrimitiveNumberEPNS_9ExecStateERd
__ZNK3KJS8JSObject18getPrimitiveNumberEPNS_9ExecStateERd
__ZNK3KJS8JSObject19implementsConstructEv
__ZNK3KJS8JSObject21implementsHasInstanceEv
__ZNK3KJS8JSObject3getEPNS_9ExecStateERKNS_10IdentifierE
......
......@@ -93,9 +93,10 @@ ArrayInstance::ArrayInstance(JSObject* prototype, const List& list)
storage->m_numValuesInVector = length;
storage->m_sparseValueMap = 0;
ListIterator it = list.begin();
for (unsigned i = 0; i < length; ++i)
storage->m_vector[i] = it++;
size_t i = 0;
List::const_iterator end = list.end();
for (List::const_iterator it = list.begin(); it != end; ++it, ++i)
storage->m_vector[i] = *it;
m_storage = storage;
......
......@@ -164,7 +164,8 @@ JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, cons
int n = 0;
JSValue *curArg = thisObj;
JSObject *curObj = static_cast<JSObject *>(thisObj);
ListIterator it = args.begin();
List::const_iterator it = args.begin();
List::const_iterator end = args.end();
for (;;) {
if (curArg->isObject() &&
curObj->inherits(&ArrayInstance::info)) {
......@@ -182,10 +183,11 @@ JSValue* ArrayProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, cons
arr->put(exec, n, curArg);
n++;
}
if (it == args.end())
if (it == end)
break;
curArg = *it;
curObj = static_cast<JSObject *>(it++); // may be 0
curObj = static_cast<JSObject*>(curArg); // may be 0
++it;
}
arr->put(exec, exec->propertyNames().length, jsNumber(n), DontEnum | DontDelete);
......
......@@ -105,7 +105,7 @@ JSObject *BooleanObjectImp::construct(ExecState *exec, const List &args)
bool b;
if (args.size() > 0)
b = args.begin()->toBoolean(exec);
b = args[0]->toBoolean(exec);
else
b = false;
......
......@@ -273,8 +273,8 @@ IndexToNameMap::IndexToNameMap(FunctionImp* func, const List& args)
this->size = args.size();
int i = 0;
ListIterator iterator = args.begin();
for (; iterator != args.end(); i++, iterator++)
List::const_iterator end = args.end();
for (List::const_iterator it = args.begin(); it != end; ++i, ++it)
_map[i] = func->getParameterName(i); // null if there is no corresponding parameter
}
......@@ -338,10 +338,10 @@ indexToNameMap(func, args)
putDirect(exec->propertyNames().length, args.size(), DontEnum);
int i = 0;
ListIterator iterator = args.begin();
for (; iterator != args.end(); i++, iterator++) {
List::const_iterator end = args.end();
for (List::const_iterator it = args.begin(); it != end; ++it, ++i) {
if (!indexToNameMap.isMapped(Identifier::from(i))) {
JSObject::put(exec, Identifier::from(i), *iterator, DontEnum);
JSObject::put(exec, Identifier::from(i), *it, DontEnum);
}
}
}
......
......@@ -138,7 +138,7 @@ JSValue* FunctionProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, c
callThis = thisArg->toObject(exec);
List argsTail;
args.slice(1, argsTail);
args.getSlice(1, argsTail);
result = func->call(exec, callThis, argsTail);
}
break;
......
......@@ -21,281 +21,60 @@
#include "config.h"
#include "list.h"
#include "internal.h"
#include <algorithm>
#define DUMP_STATISTICS 0
using std::min;
namespace KJS {
// tunable parameters
const int poolSize = 512;
const int inlineValuesSize = 5;
enum ListImpState { unusedInPool = 0, usedInPool, usedOnHeap, immortal };
struct ListImp : ListImpBase
{
ListImpState state;
int capacity;
JSValue** overflow;
union {
JSValue *values[inlineValuesSize];
ListImp *nextInFreeList;
};
#if DUMP_STATISTICS
int sizeHighWaterMark;
#endif
void markValues();
};
struct HeapListImp : ListImp
{
HeapListImp *nextInHeapList;
HeapListImp *prevInHeapList;
};
static ListImp pool[poolSize];
static ListImp *poolFreeList;
static HeapListImp *heapList;
static int poolUsed;
#if DUMP_STATISTICS
static int numLists;
static int numListsHighWaterMark;
static int listSizeHighWaterMark;
static int numListsDestroyed;
static int numListsBiggerThan[17];
struct ListStatisticsExitLogger { ~ListStatisticsExitLogger(); };
static ListStatisticsExitLogger logger;
ListStatisticsExitLogger::~ListStatisticsExitLogger()
void List::getSlice(int startIndex, List& result) const
{
printf("\nKJS::List statistics:\n\n");
printf("%d lists were allocated\n", numLists);
printf("%d lists was the high water mark\n", numListsHighWaterMark);
printf("largest list had %d elements\n", listSizeHighWaterMark);
if (numListsDestroyed) {
putc('\n', stdout);
for (int i = 0; i < 17; i++) {
printf("%.1f%% of the lists (%d) had more than %d element%s\n",
100.0 * numListsBiggerThan[i] / numListsDestroyed,
numListsBiggerThan[i],
i, i == 1 ? "" : "s");
}
putc('\n', stdout);
}
const_iterator start = min(begin() + startIndex, end());
result.m_vector.appendRange(start, end());
}
#endif
inline void ListImp::markValues()
{
int inlineSize = min(size, inlineValuesSize);
for (int i = 0; i != inlineSize; ++i) {
if (!values[i]->marked()) {
values[i]->mark();
}
}
int overflowSize = size - inlineSize;
for (int i = 0; i != overflowSize; ++i) {
if (!overflow[i]->marked()) {
overflow[i]->mark();
}
}
}
void List::markProtectedLists()
{
int seen = 0;
int used = poolUsed;
for (int i = 0; i < poolSize && seen < used; i++) {
if (pool[i].state == usedInPool) {
seen++;
if (pool[i].valueRefCount > 0) {
pool[i].markValues();
}
}
}
for (HeapListImp *l = heapList; l; l = l->nextInHeapList) {
if (l->valueRefCount > 0) {
l->markValues();
}
}
}
static inline ListImp *allocateListImp()
const List& List::empty()
{
ASSERT(JSLock::lockCount() > 0);
// Find a free one in the pool.
if (poolUsed < poolSize) {
ListImp *imp = poolFreeList ? poolFreeList : &pool[0];
poolFreeList = imp->nextInFreeList ? imp->nextInFreeList : imp + 1;
imp->state = usedInPool;
poolUsed++;
return imp;
}
HeapListImp *imp = new HeapListImp;
imp->state = usedOnHeap;
// link into heap list
if (heapList) {
heapList->prevInHeapList = imp;
}
imp->nextInHeapList = heapList;
imp->prevInHeapList = NULL;
heapList = imp;
return imp;
static const List staticList;
return staticList;
}
List::List() : _impBase(allocateListImp())
List::ListSet& List::markSet()
{
ListImp *imp = static_cast<ListImp *>(_impBase);
imp->size = 0;
imp->refCount = 1;
imp->valueRefCount = 1;
imp->capacity = 0;
imp->overflow = 0;
#if DUMP_STATISTICS
if (++numLists > numListsHighWaterMark)
numListsHighWaterMark = numLists;
imp->sizeHighWaterMark = 0;
#endif
static ListSet staticMarkSet;
return staticMarkSet;
}
void List::markValues()
void List::markProtectedListsSlowCase()
{
static_cast<ListImp *>(_impBase)->markValues();
}
void List::release()
{
ASSERT(JSLock::lockCount() > 0);
ListImp *imp = static_cast<ListImp *>(_impBase);
#if DUMP_STATISTICS
--numLists;
++numListsDestroyed;
for (int i = 0; i < 17; i++)
if (imp->sizeHighWaterMark > i)
++numListsBiggerThan[i];
#endif
delete [] imp->overflow;
imp->overflow = 0;
if (imp->state == usedInPool) {
imp->state = unusedInPool;
imp->nextInFreeList = poolFreeList;
poolFreeList = imp;
poolUsed--;
} else {
ASSERT(imp->state == usedOnHeap);
HeapListImp *list = static_cast<HeapListImp *>(imp);
// unlink from heap list
if (!list->prevInHeapList) {
heapList = list->nextInHeapList;
if (heapList) {
heapList->prevInHeapList = NULL;
}
} else {
list->prevInHeapList->nextInHeapList = list->nextInHeapList;
if (list->nextInHeapList) {
list->nextInHeapList->prevInHeapList = list->prevInHeapList;
}
ListSet::iterator end = markSet().end();
for (ListSet::iterator it = markSet().begin(); it != end; ++it) {
List* list = *it;
iterator end2 = list->end();
for (iterator it2 = list->begin(); it2 != end2; ++it2) {
JSValue* v = *it2;
if (!v->marked())
v->mark();
}
delete list;
}
}
JSValue *List::at(int i) const
void List::expandAndAppend(JSValue* v)
{
ListImp *imp = static_cast<ListImp *>(_impBase);
if ((unsigned)i >= (unsigned)imp->size)
return jsUndefined();
if (i < inlineValuesSize)
return imp->values[i];
return imp->overflow[i - inlineValuesSize];
}
void List::clear()
{
_impBase->size = 0;
}
void List::append(JSValue *v)
{
ASSERT(JSLock::lockCount() > 0);
ASSERT(m_vector.size() == m_vector.capacity());
ListImp *imp = static_cast<ListImp *>(_impBase);
int i = imp->size++;
#if DUMP_STATISTICS
if (imp->size > listSizeHighWaterMark)
listSizeHighWaterMark = imp->size;
#endif
if (i < inlineValuesSize) {
imp->values[i] = v;
return;
}
// 4x growth would be excessive for a normal vector, but it's OK for Lists
// because they're short-lived.
m_vector.reserveCapacity(m_vector.capacity() * 4);
if (i >= imp->capacity) {
int newCapacity = i * 2;
JSValue** newOverflow = new JSValue* [newCapacity - inlineValuesSize];
JSValue** oldOverflow = imp->overflow;
int oldOverflowSize = i - inlineValuesSize;
for (int j = 0; j != oldOverflowSize; j++)
newOverflow[j] = oldOverflow[j];
delete [] oldOverflow;
imp->overflow = newOverflow;
imp->capacity = newCapacity;
// As long as our size stays within our Vector's inline
// capacity, all our values are allocated on the stack, and
// therefore don't need explicit marking. Once our size exceeds
// our Vector's inline capacity, though, our values move to the
// heap, where they do need explicit marking.
if (!m_isInMarkSet) {
markSet().add(this);
m_isInMarkSet = true;
}
imp->overflow[i - inlineValuesSize] = v;
}
void List::slice(int startIndex, List& result) const
{
ListImp *imp = static_cast<ListImp *>(_impBase);
int size = imp->size;
int inlineSize = min(size, inlineValuesSize);
for (int i = startIndex; i < inlineSize; ++i)
result.append(imp->values[i]);
JSValue** overflow = imp->overflow;
int overflowSize = size - inlineSize;
for (int i = 0; i < overflowSize; ++i)
result.append(overflow[i]);
}
const List& List::empty()
{
static List* staticEmptyList = new List;
return *staticEmptyList;
m_vector.uncheckedAppend(v);
}
} // namespace KJS
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2003 Apple Computer, Inc.
* Copyright (C) 2003, 2007 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -23,148 +22,99 @@
#ifndef KJS_LIST_H
#define KJS_LIST_H
#include "value.h"
#include <kjs/value.h>
#include <wtf/HashSet.h>
#include <wtf/Noncopyable.h>
#include <wtf/Vector.h>
namespace KJS {
struct ListImpBase {
int size;
int refCount;
int valueRefCount; // FIXME: Get rid of this.
};
class JSValue;
class List;
class ListIterator;
/**
* @short Native list type.
*
* List is a native ECMAScript type. List values are only used for
* intermediate results of expression evaluation and cannot be stored
* as properties of objects.
*/
class List : Noncopyable {
public:
List();
~List() { deref(); }
/**
* Append an object to the end of the list.
*
* @param val Pointer to object.
*/
void append(JSValue *val);
/**
* Remove all elements from the list.
*/
void clear();
void reset() { deref(); ++(_impBase = empty()._impBase)->refCount; }
/**
* Make a copy of the list, starting from startIndex.
*/
void slice(int startIndex, List& result) const;
/**
* @return true if the list is empty. false otherwise.
*/
bool isEmpty() const { return _impBase->size == 0; }
/**
* @return the current size of the list.
*/
int size() const { return _impBase->size; }
/**
* @return A KJS::ListIterator pointing to the first element.
*/
ListIterator begin() const;
/**
* @return A KJS::ListIterator pointing to the last element.
*/
ListIterator end() const;
/**
* Retrieve an element at an indexed position. If you want to iterate
* trough the whole list using KJS::ListIterator will be faster.
*
* @param i List index.
* @return Return the element at position i. KJS::Undefined if the
* index is out of range.
*/
JSValue *at(int i) const;
/**
* Equivalent to at.
*/
JSValue *operator[](int i) const { return at(i); }
/**
* Returns a pointer to a static instance of an empty list. Useful if a
* function has a KJS::List parameter.
*/
static const List &empty();
static void markProtectedLists();
private:
ListImpBase *_impBase;
void deref() { --_impBase->valueRefCount; if (--_impBase->refCount == 0) release(); }
typedef Vector<JSValue*, 8> VectorType;
typedef HashSet<List*> ListSet;
void release();
void markValues();
};
/**
* @short Iterator for KJS::List objects.
*/
class ListIterator {
public:
/**
* Construct an iterator that points to the first element of the list.
* @param l The list the iterator will operate on.
*/
ListIterator(const List &l) : _list(&l), _i(0) { }
ListIterator(const List &l, int index) : _list(&l), _i(index) { }
/**
* Dereference the iterator.
* @return A pointer to the element the iterator operates on.
*/
JSValue *operator->() const { return _list->at(_i); }
JSValue *operator*() const { return _list->at(_i); }
/**
* Prefix increment operator.
* @return The element after the increment.
*/
JSValue *operator++() { return _list->at(++_i); }
/**
* Postfix increment operator.
*/
JSValue *operator++(int) { return _list->at(_i++); }
/**
* Prefix decrement operator.
*/
JSValue *operator--() { return _list->at(--_i); }
/**
* Postfix decrement operator.
*/
JSValue *operator--(int) { return _list->at(_i--); }
/**
* Compare the iterator with another one.
* @return True if the two iterators operate on the same list element.
* False otherwise.
*/
bool operator==(const ListIterator &it) const { return _i == it._i; }
/**
* Check for inequality with another iterator.
* @return True if the two iterators operate on different list elements.
*/
bool operator!=(const ListIterator &it) const { return _i != it